aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/include/utils/datetime.h17
-rw-r--r--src/interfaces/ecpg/pgtypeslib/datetime.c4
-rw-r--r--src/interfaces/ecpg/pgtypeslib/dt.h17
-rw-r--r--src/interfaces/ecpg/pgtypeslib/dt_common.c44
-rw-r--r--src/interfaces/ecpg/pgtypeslib/interval.c2
-rw-r--r--src/interfaces/ecpg/pgtypeslib/timestamp.c2
-rw-r--r--src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c22
-rw-r--r--src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout19
-rw-r--r--src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc10
-rw-r--r--src/test/regress/expected/interval.out7
-rw-r--r--src/test/regress/sql/interval.sql2
11 files changed, 111 insertions, 35 deletions
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 3cd921a0dbb..02e63b3bfc6 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -188,12 +188,17 @@ struct tzEntry;
#define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
#define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_ALL_SECS_M)
-#define MAXDATELEN 63 /* maximum possible length of an input date
- * string (not counting tr. null) */
-#define MAXDATEFIELDS 25 /* maximum possible number of fields in a date
- * string */
-#define TOKMAXLEN 10 /* only this many chars are stored in
- * datetktbl */
+/*
+ * Working buffer size for input and output of interval, timestamp, etc.
+ * Inputs that need more working space will be rejected early. Longer outputs
+ * will overrun buffers, so this must suffice for all possible output. As of
+ * this writing, interval_out() needs the most space at ~90 bytes.
+ */
+#define MAXDATELEN 128
+/* maximum possible number of fields in a date string */
+#define MAXDATEFIELDS 25
+/* only this many chars are stored in datetktbl */
+#define TOKMAXLEN 10
/* keep this struct small; it gets used a lot */
typedef struct
diff --git a/src/interfaces/ecpg/pgtypeslib/datetime.c b/src/interfaces/ecpg/pgtypeslib/datetime.c
index 6600759220e..a271cdd7d15 100644
--- a/src/interfaces/ecpg/pgtypeslib/datetime.c
+++ b/src/interfaces/ecpg/pgtypeslib/datetime.c
@@ -60,14 +60,14 @@ PGTYPESdate_from_asc(char *str, char **endptr)
int nf;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
- char lowstr[MAXDATELEN + 1];
+ char lowstr[MAXDATELEN + MAXDATEFIELDS];
char *realptr;
char **ptr = (endptr != NULL) ? endptr : &realptr;
bool EuroDates = FALSE;
errno = 0;
- if (strlen(str) >= sizeof(lowstr))
+ if (strlen(str) > MAXDATELEN)
{
errno = PGTYPES_DATE_BAD_DATE;
return INT_MIN;
diff --git a/src/interfaces/ecpg/pgtypeslib/dt.h b/src/interfaces/ecpg/pgtypeslib/dt.h
index dfe6f9e6872..2780593c5e5 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt.h
+++ b/src/interfaces/ecpg/pgtypeslib/dt.h
@@ -192,12 +192,17 @@ typedef double fsec_t;
#define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
#define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND))
-#define MAXDATELEN 63 /* maximum possible length of an input date
- * string (not counting tr. null) */
-#define MAXDATEFIELDS 25 /* maximum possible number of fields in a date
- * string */
-#define TOKMAXLEN 10 /* only this many chars are stored in
- * datetktbl */
+/*
+ * Working buffer size for input and output of interval, timestamp, etc.
+ * Inputs that need more working space will be rejected early. Longer outputs
+ * will overrun buffers, so this must suffice for all possible output. As of
+ * this writing, PGTYPESinterval_to_asc() needs the most space at ~90 bytes.
+ */
+#define MAXDATELEN 128
+/* maximum possible number of fields in a date string */
+#define MAXDATEFIELDS 25
+/* only this many chars are stored in datetktbl */
+#define TOKMAXLEN 10
/* keep this struct small; it gets used a lot */
typedef struct
diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
index 6b89e4a67eb..18178dd34b8 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -1171,15 +1171,22 @@ DecodeNumberField(int len, char *str, int fmask,
if ((cp = strchr(str, '.')) != NULL)
{
#ifdef HAVE_INT64_TIMESTAMP
- char fstr[MAXDATELEN + 1];
+ char fstr[7];
+ int i;
+
+ cp++;
/*
* OK, we have at most six digits to care about. Let's construct a
- * string and then do the conversion to an integer.
+ * string with those digits, zero-padded on the right, and then do
+ * the conversion to an integer.
+ *
+ * XXX This truncates the seventh digit, unlike rounding it as do
+ * the backend and the !HAVE_INT64_TIMESTAMP case.
*/
- strcpy(fstr, (cp + 1));
- strcpy(fstr + strlen(fstr), "000000");
- *(fstr + 6) = '\0';
+ for (i = 0; i < 6; i++)
+ fstr[i] = *cp != '\0' ? *cp++ : '0';
+ fstr[i] = '\0';
*fsec = strtol(fstr, NULL, 10);
#else
*fsec = strtod(cp, NULL);
@@ -1531,15 +1538,22 @@ DecodeTime(char *str, int *tmask, struct tm * tm, fsec_t *fsec)
else if (*cp == '.')
{
#ifdef HAVE_INT64_TIMESTAMP
- char fstr[MAXDATELEN + 1];
+ char fstr[7];
+ int i;
+
+ cp++;
/*
- * OK, we have at most six digits to work with. Let's construct a
- * string and then do the conversion to an integer.
+ * OK, we have at most six digits to care about. Let's construct a
+ * string with those digits, zero-padded on the right, and then do
+ * the conversion to an integer.
+ *
+ * XXX This truncates the seventh digit, unlike rounding it as do
+ * the backend and the !HAVE_INT64_TIMESTAMP case.
*/
- strncpy(fstr, (cp + 1), 7);
- strcpy(fstr + strlen(fstr), "000000");
- *(fstr + 6) = '\0';
+ for (i = 0; i < 6; i++)
+ fstr[i] = *cp != '\0' ? *cp++ : '0';
+ fstr[i] = '\0';
*fsec = strtol(fstr, &cp, 10);
#else
str = cp;
@@ -1665,6 +1679,9 @@ DecodePosixTimezone(char *str, int *tzp)
* DTK_NUMBER can hold date fields (yy.ddd)
* DTK_STRING can hold months (January) and time zones (PST)
* DTK_DATE can hold Posix time zones (GMT-8)
+ *
+ * The "lowstr" work buffer must have at least strlen(timestr) + MAXDATEFIELDS
+ * bytes of space. On output, field[] entries will point into it.
*/
int
ParseDateTime(char *timestr, char *lowstr,
@@ -1677,7 +1694,10 @@ ParseDateTime(char *timestr, char *lowstr,
/* outer loop through fields */
while (*(*endstr) != '\0')
{
+ /* Record start of current field */
field[nf] = lp;
+ if (nf >= MAXDATEFIELDS)
+ return -1;
/* leading digit? then date or time */
if (isdigit((unsigned char) *(*endstr)))
@@ -1818,8 +1838,6 @@ ParseDateTime(char *timestr, char *lowstr,
/* force in a delimiter after each field */
*lp++ = '\0';
nf++;
- if (nf > MAXDATEFIELDS)
- return -1;
}
*numfields = nf;
diff --git a/src/interfaces/ecpg/pgtypeslib/interval.c b/src/interfaces/ecpg/pgtypeslib/interval.c
index efa775de15a..53fc11f1487 100644
--- a/src/interfaces/ecpg/pgtypeslib/interval.c
+++ b/src/interfaces/ecpg/pgtypeslib/interval.c
@@ -1091,7 +1091,7 @@ PGTYPESinterval_from_asc(char *str, char **endptr)
tm->tm_sec = 0;
fsec = 0;
- if (strlen(str) >= sizeof(lowstr))
+ if (strlen(str) > MAXDATELEN)
{
errno = PGTYPES_INTVL_BAD_INTERVAL;
return NULL;
diff --git a/src/interfaces/ecpg/pgtypeslib/timestamp.c b/src/interfaces/ecpg/pgtypeslib/timestamp.c
index 3770bd2925b..7b8b6d3e627 100644
--- a/src/interfaces/ecpg/pgtypeslib/timestamp.c
+++ b/src/interfaces/ecpg/pgtypeslib/timestamp.c
@@ -297,7 +297,7 @@ PGTYPEStimestamp_from_asc(char *str, char **endptr)
char *realptr;
char **ptr = (endptr != NULL) ? endptr : &realptr;
- if (strlen(str) >= sizeof(lowstr))
+ if (strlen(str) > MAXDATELEN)
{
errno = PGTYPES_TS_BAD_TIMESTAMP;
return (noresult);
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c
index d3ebb0e106c..0ba1936f1db 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c
@@ -45,6 +45,15 @@ char *dates[] = { "19990108foobar",
"1999.008",
"J2451187",
"January 8, 99 BC",
+ /*
+ * Maximize space usage in ParseDateTime() with 25
+ * (MAXDATEFIELDS) fields and 128 (MAXDATELEN) total length.
+ */
+ "........................Xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ /* 26 fields */
+ ".........................aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
NULL };
/* do not conflict with libc "times" symbol */
@@ -52,6 +61,7 @@ static char *times[] = { "0:04",
"1:59 PDT",
"13:24:40 -8:00",
"13:24:40.495+3",
+ "13:24:40.123456789+3",
NULL };
char *intervals[] = { "1 minute",
@@ -73,22 +83,22 @@ main(void)
-#line 52 "dt_test2.pgc"
+#line 62 "dt_test2.pgc"
date date1 ;
-#line 53 "dt_test2.pgc"
+#line 63 "dt_test2.pgc"
timestamp ts1 , ts2 ;
-#line 54 "dt_test2.pgc"
+#line 64 "dt_test2.pgc"
char * text ;
-#line 55 "dt_test2.pgc"
+#line 65 "dt_test2.pgc"
interval * i1 ;
-#line 56 "dt_test2.pgc"
+#line 66 "dt_test2.pgc"
date * dc ;
/* exec sql end declare section */
-#line 57 "dt_test2.pgc"
+#line 67 "dt_test2.pgc"
int i, j;
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout
index 24e9d26dfeb..9a4587b498e 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout
@@ -8,85 +8,104 @@ TS[3,0]: 1999-01-08 00:04:00
TS[3,1]: 1999-01-08 01:59:00
TS[3,2]: 1999-01-08 13:24:40
TS[3,3]: 1999-01-08 13:24:40.495
+TS[3,4]: 1999-01-08 13:24:40.123456
Date[4]: 1999-01-08 (N - F)
TS[4,0]: 1999-01-08 00:04:00
TS[4,1]: 1999-01-08 01:59:00
TS[4,2]: 1999-01-08 13:24:40
TS[4,3]: 1999-01-08 13:24:40.495
+TS[4,4]: 1999-01-08 13:24:40.123456
Date[5]: 1999-01-08 (N - F)
TS[5,0]: 1999-01-08 00:04:00
TS[5,1]: 1999-01-08 01:59:00
TS[5,2]: 1999-01-08 13:24:40
TS[5,3]: 1999-01-08 13:24:40.495
+TS[5,4]: 1999-01-08 13:24:40.123456
Date[6]: 1999-01-18 (N - F)
TS[6,0]: 1999-01-18 00:04:00
TS[6,1]: 1999-01-18 01:59:00
TS[6,2]: 1999-01-18 13:24:40
TS[6,3]: 1999-01-18 13:24:40.495
+TS[6,4]: 1999-01-18 13:24:40.123456
Date[7]: 2003-01-02 (N - F)
TS[7,0]: 2003-01-02 00:04:00
TS[7,1]: 2003-01-02 01:59:00
TS[7,2]: 2003-01-02 13:24:40
TS[7,3]: 2003-01-02 13:24:40.495
+TS[7,4]: 2003-01-02 13:24:40.123456
Date[8]: 1999-01-08 (N - F)
TS[8,0]: 1999-01-08 00:04:00
TS[8,1]: 1999-01-08 01:59:00
TS[8,2]: 1999-01-08 13:24:40
TS[8,3]: 1999-01-08 13:24:40.495
+TS[8,4]: 1999-01-08 13:24:40.123456
Date[9]: 1999-01-08 (N - F)
TS[9,0]: 1999-01-08 00:04:00
TS[9,1]: 1999-01-08 01:59:00
TS[9,2]: 1999-01-08 13:24:40
TS[9,3]: 1999-01-08 13:24:40.495
+TS[9,4]: 1999-01-08 13:24:40.123456
Date[10]: 1999-01-08 (N - F)
TS[10,0]: 1999-01-08 00:04:00
TS[10,1]: 1999-01-08 01:59:00
TS[10,2]: 1999-01-08 13:24:40
TS[10,3]: 1999-01-08 13:24:40.495
+TS[10,4]: 1999-01-08 13:24:40.123456
Date[11]: 1999-01-08 (N - F)
TS[11,0]: 1999-01-08 00:04:00
TS[11,1]: 1999-01-08 01:59:00
TS[11,2]: 1999-01-08 13:24:40
TS[11,3]: 1999-01-08 13:24:40.495
+TS[11,4]: 1999-01-08 13:24:40.123456
Date[12]: 1999-01-08 (N - F)
TS[12,0]: 1999-01-08 00:04:00
TS[12,1]: 1999-01-08 01:59:00
TS[12,2]: 1999-01-08 13:24:40
TS[12,3]: 1999-01-08 13:24:40.495
+TS[12,4]: 1999-01-08 13:24:40.123456
Date[13]: 2006-01-08 (N - F)
TS[13,0]: 2006-01-08 00:04:00
TS[13,1]: 2006-01-08 01:59:00
TS[13,2]: 2006-01-08 13:24:40
TS[13,3]: 2006-01-08 13:24:40.495
+TS[13,4]: 2006-01-08 13:24:40.123456
Date[14]: 1999-01-08 (N - F)
TS[14,0]: 1999-01-08 00:04:00
TS[14,1]: 1999-01-08 01:59:00
TS[14,2]: 1999-01-08 13:24:40
TS[14,3]: 1999-01-08 13:24:40.495
+TS[14,4]: 1999-01-08 13:24:40.123456
Date[15]: 1999-01-08 (N - F)
TS[15,0]: 1999-01-08 00:04:00
TS[15,1]: 1999-01-08 01:59:00
TS[15,2]: 1999-01-08 13:24:40
TS[15,3]: 1999-01-08 13:24:40.495
+TS[15,4]: 1999-01-08 13:24:40.123456
Date[16]: 1999-01-08 (N - F)
TS[16,0]: 1999-01-08 00:04:00
TS[16,1]: 1999-01-08 01:59:00
TS[16,2]: 1999-01-08 13:24:40
TS[16,3]: 1999-01-08 13:24:40.495
+TS[16,4]: 1999-01-08 13:24:40.123456
Date[17]: 1999-01-08 (N - F)
TS[17,0]: 1999-01-08 00:04:00
TS[17,1]: 1999-01-08 01:59:00
TS[17,2]: 1999-01-08 13:24:40
TS[17,3]: 1999-01-08 13:24:40.495
+TS[17,4]: 1999-01-08 13:24:40.123456
Date[18]: 1999-01-08 (N - F)
TS[18,0]: 1999-01-08 00:04:00
TS[18,1]: 1999-01-08 01:59:00
TS[18,2]: 1999-01-08 13:24:40
TS[18,3]: 1999-01-08 13:24:40.495
+TS[18,4]: 1999-01-08 13:24:40.123456
Date[19]: 0099-01-08 BC (N - F)
TS[19,0]: 0099-01-08 00:04:00 BC
TS[19,1]: 0099-01-08 01:59:00 BC
TS[19,2]: 0099-01-08 13:24:40 BC
+TS[19,4]: 0099-01-08 13:24:40.123456 BC
+Date[20]: - (N - T)
+Date[21]: - (N - T)
interval[0]: @ 1 min
interval_copy[0]: @ 1 min
interval[1]: @ 1 day 12 hours 59 mins 10 secs
diff --git a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
index 0edf012fd11..a127dd93a66 100644
--- a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
+++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc
@@ -27,6 +27,15 @@ char *dates[] = { "19990108foobar",
"1999.008",
"J2451187",
"January 8, 99 BC",
+ /*
+ * Maximize space usage in ParseDateTime() with 25
+ * (MAXDATEFIELDS) fields and 128 (MAXDATELEN) total length.
+ */
+ "........................Xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ /* 26 fields */
+ ".........................aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
NULL };
/* do not conflict with libc "times" symbol */
@@ -34,6 +43,7 @@ static char *times[] = { "0:04",
"1:59 PDT",
"13:24:40 -8:00",
"13:24:40.495+3",
+ "13:24:40.123456789+3",
NULL };
char *intervals[] = { "1 minute",
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index 3bf221187bc..99fd0ca4900 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -306,6 +306,13 @@ select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31
@ 4541 years 4 mons 4 days 17 mins 31 secs
(1 row)
+-- test long interval output
+select '100000000y 10mon -1000000000d -1000000000h -10min -10.000001s ago'::interval;
+ interval
+-------------------------------------------------------------------------------------------
+ @ 100000000 years 10 mons -1000000000 days -1000000000 hours -10 mins -10.000001 secs ago
+(1 row)
+
-- test justify_hours() and justify_days()
SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds";
6 mons 5 days 4 hours 3 mins 2 seconds
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index f1da4c29117..7cee2864def 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -108,6 +108,8 @@ select avg(f1) from interval_tbl;
-- test long interval input
select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 seconds'::interval;
+-- test long interval output
+select '100000000y 10mon -1000000000d -1000000000h -10min -10.000001s ago'::interval;
-- test justify_hours() and justify_days()