Fix off-by-one error in PGTYPEStimestamp_fmt_asc
authorTomas Vondra <[email protected]>
Sat, 30 Nov 2019 13:51:27 +0000 (14:51 +0100)
committerTomas Vondra <[email protected]>
Sat, 30 Nov 2019 14:16:58 +0000 (15:16 +0100)
When using %b or %B patterns to format a date, the code was simply using
tm_mon as an index into array of month names. But that is wrong, because
tm_mon is 1-based, while array indexes are 0-based. The result is we
either use name of the next month, or a segfault (for December).

Fix by subtracting 1 from tm_mon for both patterns, and add a regression
test triggering the issue. Backpatch to all supported versions (the bug
is there far longer, since at least 2003).

Reported-by: Paul Spencer
Backpatch-through: 9.4
Discussion: https://postgr.es/m/16143-0d861eb8688d3fef%40postgresql.org

src/interfaces/ecpg/pgtypeslib/timestamp.c
src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c
src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr
src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stdout
src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc

index b0f9bf15219be5aabef57cf5fc6cd81ad9541fa1..34383f3312aa2100185fbb47f2bfaaccc70855f2 100644 (file)
@@ -416,13 +416,13 @@ dttofmtasc_replace(timestamp * ts, date dDate, int dow, struct tm * tm,
                    /* XXX should be locale aware */
                case 'b':
                case 'h':
-                   replace_val.str_val = months[tm->tm_mon];
+                   replace_val.str_val = months[tm->tm_mon - 1];
                    replace_type = PGTYPES_TYPE_STRING_CONSTANT;
                    break;
                    /* the full name name of the month */
                    /* XXX should be locale aware */
                case 'B':
-                   replace_val.str_val = pgtypes_date_months[tm->tm_mon];
+                   replace_val.str_val = pgtypes_date_months[tm->tm_mon - 1];
                    replace_type = PGTYPES_TYPE_STRING_CONSTANT;
                    break;
 
index 54de05e528bb429bb555efb15a7e1df9ac817748..d5c3ec08e31440f1e5796d5528f72cb43f233992 100644 (file)
@@ -436,17 +436,33 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
    printf("timestamp_defmt_asc(%s, %s) = %s, error: %d\n", in, fmt, text, i);
    PGTYPESchar_free(text);
 
+   out = (char*) malloc(64);
+   fmt = "%a %b %d %H:%M:%S %Y";
+   in =  "Mon Dec 30 17:28:44 2019";
+   i = PGTYPEStimestamp_defmt_asc(in, fmt, &ts1);
+   i = PGTYPEStimestamp_fmt_asc(&ts1, out, 63, fmt);
+   printf("timestamp_defmt_asc(%s, %s) = %s, error: %d\n", in, fmt, out, i);
+   free(out);
+
+   out = (char*) malloc(64);
+   fmt = "%a %b %d %H:%M:%S %Y";
+   in =  "Mon December 30 17:28:44 2019";
+   i = PGTYPEStimestamp_defmt_asc(in, fmt, &ts1);
+   i = PGTYPEStimestamp_fmt_asc(&ts1, out, 63, fmt);
+   printf("timestamp_defmt_asc(%s, %s) = %s, error: %d\n", in, fmt, out, i);
+   free(out);
+
    { ECPGtrans(__LINE__, NULL, "rollback");
-#line 365 "dt_test.pgc"
+#line 381 "dt_test.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 365 "dt_test.pgc"
+#line 381 "dt_test.pgc"
 
         { ECPGdisconnect(__LINE__, "CURRENT");
-#line 366 "dt_test.pgc"
+#line 382 "dt_test.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 366 "dt_test.pgc"
+#line 382 "dt_test.pgc"
 
 
    return (0);
index 14b2aaf485f3f3698dac663b6c62b0784293c6bb..80a9b29f44be67ad4b2dd28505a260ed1200bdfc 100644 (file)
@@ -42,7 +42,7 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_get_data on line 38: RESULT: 2000-07-12 17:34:29 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 365: action "rollback"; connection "regress1"
+[NO_PID]: ECPGtrans on line 381: action "rollback"; connection "regress1"
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection regress1 closed
 [NO_PID]: sqlca: code: 0, state: 00000
index 823b6e0062352a2f072f86fab86673603c4948ea..4b582fd7a28029c5a229db27ed59241b786cf590 100644 (file)
@@ -48,3 +48,5 @@ timestamp_defmt_asc(  1976, July 14. Time: 9:15am, %Y,   %B %d. Time: %I:%M %p)
 timestamp_defmt_asc(  1976, July 14. Time: 9:15 am, %Y,   %B %d. Time: %I:%M%p) = 1976-07-14 09:15:00, error: 0
 timestamp_defmt_asc(  1976, P.M. July 14. Time: 9:15, %Y, %P  %B %d. Time: %I:%M) = 1976-07-14 21:15:00, error: 0
 timestamp_defmt_asc(1234567890, %s) = 2009-02-13 23:31:30, error: 0
+timestamp_defmt_asc(Mon Dec 30 17:28:44 2019, %a %b %d %H:%M:%S %Y) = Mon Dec 30 17:28:44 2019, error: 0
+timestamp_defmt_asc(Mon December 30 17:28:44 2019, %a %b %d %H:%M:%S %Y) = Mon Dec 30 17:28:44 2019, error: 0
index 6cc2983c3bcfffbb766dff9c1b797d3915b3419b..23eb7374d0e83158e9be4e75ab14eddd30913e67 100644 (file)
@@ -362,6 +362,22 @@ main(void)
    printf("timestamp_defmt_asc(%s, %s) = %s, error: %d\n", in, fmt, text, i);
    PGTYPESchar_free(text);
 
+   out = (char*) malloc(64);
+   fmt = "%a %b %d %H:%M:%S %Y";
+   in =  "Mon Dec 30 17:28:44 2019";
+   i = PGTYPEStimestamp_defmt_asc(in, fmt, &ts1);
+   i = PGTYPEStimestamp_fmt_asc(&ts1, out, 63, fmt);
+   printf("timestamp_defmt_asc(%s, %s) = %s, error: %d\n", in, fmt, out, i);
+   free(out);
+
+   out = (char*) malloc(64);
+   fmt = "%a %b %d %H:%M:%S %Y";
+   in =  "Mon December 30 17:28:44 2019";
+   i = PGTYPEStimestamp_defmt_asc(in, fmt, &ts1);
+   i = PGTYPEStimestamp_fmt_asc(&ts1, out, 63, fmt);
+   printf("timestamp_defmt_asc(%s, %s) = %s, error: %d\n", in, fmt, out, i);
+   free(out);
+
    exec sql rollback;
         exec sql disconnect;