aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Meskes <meskes@postgresql.org>2008-11-26 16:31:02 +0000
committerMichael Meskes <meskes@postgresql.org>2008-11-26 16:31:02 +0000
commit7c5a561f31339ac3cfedb2dab066eca331dd75ac (patch)
treeda0d4acffcd03be4ed724514a684955e21c48426 /src
parentcbb3e1cda7b6d571d5fe1501495f4ed8fb8ab965 (diff)
downloadpostgresql-7c5a561f31339ac3cfedb2dab066eca331dd75ac.tar.gz
postgresql-7c5a561f31339ac3cfedb2dab066eca331dd75ac.zip
Applied patch by Ron Mayer <rm_pg@cheapcomplexdevices.com> to merge the new
interval style into ecpg.
Diffstat (limited to 'src')
-rw-r--r--src/interfaces/ecpg/ChangeLog2
-rw-r--r--src/interfaces/ecpg/pgtypeslib/dt.h48
-rw-r--r--src/interfaces/ecpg/pgtypeslib/interval.c1064
-rw-r--r--src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c22
-rw-r--r--src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr30
-rw-r--r--src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc1
6 files changed, 791 insertions, 376 deletions
diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog
index 939b2e5914b..c6064cd0afb 100644
--- a/src/interfaces/ecpg/ChangeLog
+++ b/src/interfaces/ecpg/ChangeLog
@@ -2394,6 +2394,8 @@ Sat, 25 Oct 2008 16:34:28 +0200
Wed, 26 Nov 2008 14:09:08 +0100
- When creating a varchar struct name braces must be discarded.
+ - Applied patch by Ron Mayer <rm_pg@cheapcomplexdevices.com> to merge
+ the new interval style into ecpg.
- Set pgtypes library version to 3.1.
- Set compat library version to 3.1.
- Set ecpg library version to 6.2.
diff --git a/src/interfaces/ecpg/pgtypeslib/dt.h b/src/interfaces/ecpg/pgtypeslib/dt.h
index 47905dedfe7..0008211fad0 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt.h
+++ b/src/interfaces/ecpg/pgtypeslib/dt.h
@@ -1,4 +1,4 @@
-/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/dt.h,v 1.39 2007/11/15 21:14:45 momjian Exp $ */
+/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/dt.h,v 1.40 2008/11/26 16:31:02 meskes Exp $ */
#ifndef DT_H
#define DT_H
@@ -25,6 +25,22 @@ typedef double fsec_t;
#define USE_SQL_DATES 2
#define USE_GERMAN_DATES 3
+#define INTSTYLE_POSTGRES 0
+#define INTSTYLE_POSTGRES_VERBOSE 1
+#define INTSTYLE_SQL_STANDARD 2
+#define INTSTYLE_ISO_8601 3
+
+#define INTERVAL_FULL_RANGE (0x7FFF)
+#define INTERVAL_MASK(b) (1 << (b))
+#define MAX_INTERVAL_PRECISION 6
+
+#define DTERR_BAD_FORMAT (-1)
+#define DTERR_FIELD_OVERFLOW (-2)
+#define DTERR_MD_FIELD_OVERFLOW (-3) /* triggers hint about DateStyle */
+#define DTERR_INTERVAL_OVERFLOW (-4)
+#define DTERR_TZDISP_OVERFLOW (-5)
+
+
#define DAGO "ago"
#define EPOCH "epoch"
#define INVALID "invalid"
@@ -77,6 +93,9 @@ typedef double fsec_t;
* Furthermore, the values for YEAR, MONTH, DAY, HOUR, MINUTE, SECOND
* must be in the range 0..14 so that the associated bitmasks can fit
* into the left half of an INTERVAL's typmod value.
+ *
+ * Copy&pasted these values from src/include/utils/datetime.h
+ * 2008-11-20, changing a number of their values.
*/
#define RESERV 0
@@ -92,20 +111,23 @@ typedef double fsec_t;
#define HOUR 10
#define MINUTE 11
#define SECOND 12
-#define DOY 13
-#define DOW 14
-#define UNITS 15
-#define ADBC 16
+#define MILLISECOND 13
+#define MICROSECOND 14
+#define DOY 15
+#define DOW 16
+#define UNITS 17
+#define ADBC 18
/* these are only for relative dates */
-#define AGO 17
-#define ABS_BEFORE 18
-#define ABS_AFTER 19
+#define AGO 19
+#define ABS_BEFORE 20
+#define ABS_AFTER 21
/* generic fields to help with parsing */
-#define ISODATE 20
-#define ISOTIME 21
+#define ISODATE 22
+#define ISOTIME 23
/* reserved for unrecognized string values */
#define UNKNOWN_FIELD 31
+
/*
* Token field definitions for time parsing and decoding.
* These need to fit into the datetkn table type.
@@ -164,13 +186,13 @@ typedef double fsec_t;
/*
* Bit mask definitions for time parsing.
*/
-
+/* Copy&pasted these values from src/include/utils/datetime.h */
#define DTK_M(t) (0x01 << (t))
-
+#define DTK_ALL_SECS_M (DTK_M(SECOND) | DTK_M(MILLISECOND) | DTK_M(MICROSECOND))
#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 51 /* maximum possible length of an input date
+#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 */
diff --git a/src/interfaces/ecpg/pgtypeslib/interval.c b/src/interfaces/ecpg/pgtypeslib/interval.c
index a8f1899fdb3..58b5fefff6d 100644
--- a/src/interfaces/ecpg/pgtypeslib/interval.c
+++ b/src/interfaces/ecpg/pgtypeslib/interval.c
@@ -1,4 +1,4 @@
-/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/interval.c,v 1.37 2007/08/22 08:20:58 meskes Exp $ */
+/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/interval.c,v 1.38 2008/11/26 16:31:02 meskes Exp $ */
#include "postgres_fe.h"
#include <time.h>
@@ -13,39 +13,347 @@
#include "pgtypes_error.h"
#include "pgtypes_interval.h"
-/* DecodeInterval()
- * Interpret previously parsed fields for general time interval.
- * Return 0 if decoded and -1 if problems.
+/* copy&pasted from .../src/backend/utils/adt/datetime.c */
+static int
+strtoi(const char *nptr, char **endptr, int base)
+{
+ long val;
+
+ val = strtol(nptr, endptr, base);
+#ifdef HAVE_LONG_INT_64
+ if (val != (long) ((int32) val))
+ errno = ERANGE;
+#endif
+ return (int) val;
+}
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c
+ * and changesd struct pg_tm to struct tm
+ */
+static void
+AdjustFractSeconds(double frac, struct /*pg_*/tm * tm, fsec_t *fsec, int scale)
+{
+ int sec;
+
+ if (frac == 0)
+ return;
+ frac *= scale;
+ sec = (int) frac;
+ tm->tm_sec += sec;
+ frac -= sec;
+#ifdef HAVE_INT64_TIMESTAMP
+ *fsec += rint(frac * 1000000);
+#else
+ *fsec += frac;
+#endif
+}
+
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c
+ * and changesd struct pg_tm to struct tm
+ */
+static void
+AdjustFractDays(double frac, struct /*pg_*/tm * tm, fsec_t *fsec, int scale)
+{
+ int extra_days;
+
+ if (frac == 0)
+ return;
+ frac *= scale;
+ extra_days = (int) frac;
+ tm->tm_mday += extra_days;
+ frac -= extra_days;
+ AdjustFractSeconds(frac, tm, fsec, SECS_PER_DAY);
+}
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c */
+static int
+ParseISO8601Number(char *str, char **endptr, int *ipart, double *fpart)
+{
+ double val;
+
+ if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
+ return DTERR_BAD_FORMAT;
+ errno = 0;
+ val = strtod(str, endptr);
+ /* did we not see anything that looks like a double? */
+ if (*endptr == str || errno != 0)
+ return DTERR_BAD_FORMAT;
+ /* watch out for overflow */
+ if (val < INT_MIN || val > INT_MAX)
+ return DTERR_FIELD_OVERFLOW;
+ /* be very sure we truncate towards zero (cf dtrunc()) */
+ if (val >= 0)
+ *ipart = (int) floor(val);
+ else
+ *ipart = (int) -floor(-val);
+ *fpart = val - *ipart;
+ return 0;
+}
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c */
+static int
+ISO8601IntegerWidth(char *fieldstart)
+{
+ /* We might have had a leading '-' */
+ if (*fieldstart == '-')
+ fieldstart++;
+ return strspn(fieldstart, "0123456789");
+}
+
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c
+ * and changesd struct pg_tm to struct tm
+ */
+static inline void
+ClearPgTm(struct /*pg_*/tm *tm, fsec_t *fsec)
+{
+ tm->tm_year = 0;
+ tm->tm_mon = 0;
+ tm->tm_mday = 0;
+ tm->tm_hour = 0;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ *fsec = 0;
+}
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c
+ *
+ * * changesd struct pg_tm to struct tm
+ *
+ * * Made the function static
+ */
+static int
+DecodeISO8601Interval(char *str,
+ int *dtype, struct /*pg_*/tm * tm, fsec_t *fsec)
+{
+ bool datepart = true;
+ bool havefield = false;
+
+ *dtype = DTK_DELTA;
+ ClearPgTm(tm, fsec);
+
+ if (strlen(str) < 2 || str[0] != 'P')
+ return DTERR_BAD_FORMAT;
+
+ str++;
+ while (*str)
+ {
+ char *fieldstart;
+ int val;
+ double fval;
+ char unit;
+ int dterr;
+
+ if (*str == 'T') /* T indicates the beginning of the time part */
+ {
+ datepart = false;
+ havefield = false;
+ str++;
+ continue;
+ }
+
+ fieldstart = str;
+ dterr = ParseISO8601Number(str, &str, &val, &fval);
+ if (dterr)
+ return dterr;
+
+ /*
+ * Note: we could step off the end of the string here. Code below
+ * *must* exit the loop if unit == '\0'.
+ */
+ unit = *str++;
+
+ if (datepart)
+ {
+ switch (unit) /* before T: Y M W D */
+ {
+ case 'Y':
+ tm->tm_year += val;
+ tm->tm_mon += (fval * 12);
+ break;
+ case 'M':
+ tm->tm_mon += val;
+ AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
+ break;
+ case 'W':
+ tm->tm_mday += val * 7;
+ AdjustFractDays(fval, tm, fsec, 7);
+ break;
+ case 'D':
+ tm->tm_mday += val;
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
+ break;
+ case 'T': /* ISO 8601 4.4.3.3 Alternative Format / Basic */
+ case '\0':
+ if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
+ {
+ tm->tm_year += val / 10000;
+ tm->tm_mon += (val / 100) % 100;
+ tm->tm_mday += val % 100;
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
+ if (unit == '\0')
+ return 0;
+ datepart = false;
+ havefield = false;
+ continue;
+ }
+ /* Else fall through to extended alternative format */
+ case '-': /* ISO 8601 4.4.3.3 Alternative Format, Extended */
+ if (havefield)
+ return DTERR_BAD_FORMAT;
+
+ tm->tm_year += val;
+ tm->tm_mon += (fval * 12);
+ if (unit == '\0')
+ return 0;
+ if (unit == 'T')
+ {
+ datepart = false;
+ havefield = false;
+ continue;
+ }
+
+ dterr = ParseISO8601Number(str, &str, &val, &fval);
+ if (dterr)
+ return dterr;
+ tm->tm_mon += val;
+ AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
+ if (*str == '\0')
+ return 0;
+ if (*str == 'T')
+ {
+ datepart = false;
+ havefield = false;
+ continue;
+ }
+ if (*str != '-')
+ return DTERR_BAD_FORMAT;
+ str++;
+
+ dterr = ParseISO8601Number(str, &str, &val, &fval);
+ if (dterr)
+ return dterr;
+ tm->tm_mday += val;
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
+ if (*str == '\0')
+ return 0;
+ if (*str == 'T')
+ {
+ datepart = false;
+ havefield = false;
+ continue;
+ }
+ return DTERR_BAD_FORMAT;
+ default:
+ /* not a valid date unit suffix */
+ return DTERR_BAD_FORMAT;
+ }
+ }
+ else
+ {
+ switch (unit) /* after T: H M S */
+ {
+ case 'H':
+ tm->tm_hour += val;
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
+ break;
+ case 'M':
+ tm->tm_min += val;
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
+ break;
+ case 'S':
+ tm->tm_sec += val;
+ AdjustFractSeconds(fval, tm, fsec, 1);
+ break;
+ case '\0': /* ISO 8601 4.4.3.3 Alternative Format */
+ if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
+ {
+ tm->tm_hour += val / 10000;
+ tm->tm_min += (val / 100) % 100;
+ tm->tm_sec += val % 100;
+ AdjustFractSeconds(fval, tm, fsec, 1);
+ return 0;
+ }
+ /* Else fall through to extended alternative format */
+ case ':': /* ISO 8601 4.4.3.3 Alternative Format, Extended */
+ if (havefield)
+ return DTERR_BAD_FORMAT;
+
+ tm->tm_hour += val;
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
+ if (unit == '\0')
+ return 0;
+
+ dterr = ParseISO8601Number(str, &str, &val, &fval);
+ if (dterr)
+ return dterr;
+ tm->tm_min += val;
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
+ if (*str == '\0')
+ return 0;
+ if (*str != ':')
+ return DTERR_BAD_FORMAT;
+ str++;
+
+ dterr = ParseISO8601Number(str, &str, &val, &fval);
+ if (dterr)
+ return dterr;
+ tm->tm_sec += val;
+ AdjustFractSeconds(fval, tm, fsec, 1);
+ if (*str == '\0')
+ return 0;
+ return DTERR_BAD_FORMAT;
+
+ default:
+ /* not a valid time unit suffix */
+ return DTERR_BAD_FORMAT;
+ }
+ }
+
+ havefield = true;
+ }
+
+ return 0;
+}
+
+
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c
+ * with 3 exceptions
+ *
+ * * changesd struct pg_tm to struct tm
+ *
+ * * ECPG code called this without a 'range' parameter
+ * removed 'int range' from the argument list and
+ * places where DecodeTime is called; and added
+ * int range = INTERVAL_FULL_RANGE;
*
- * Allow "date" field DTK_DATE since this could be just
- * an unsigned floating point number. - thomas 1997-11-16
+ * * ECPG semes not to have a global IntervalStyle
+ * so added
+ * int IntervalStyle = INTSTYLE_POSTGRES;
*
- * Allow ISO-style time span, with implicit units on number of days
- * preceding an hh:mm:ss field. - thomas 1998-04-30
+ * * Assert wasn't available so removed it.
*/
int
-DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec)
+DecodeInterval(char **field, int *ftype, int nf, /*int range,*/
+ int *dtype, struct /*pg_*/tm * tm, fsec_t *fsec)
{
- int is_before = FALSE;
-
+ int IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
+ int range = INTERVAL_FULL_RANGE;
+ bool is_before = FALSE;
char *cp;
int fmask = 0,
tmask,
type;
int i;
+ int dterr;
int val;
double fval;
*dtype = DTK_DELTA;
-
type = IGNORE_DTF;
- tm->tm_year = 0;
- tm->tm_mon = 0;
- tm->tm_mday = 0;
- tm->tm_hour = 0;
- tm->tm_min = 0;
- tm->tm_sec = 0;
- *fsec = 0;
+ ClearPgTm(tm,fsec);
/* read through list backwards to pick up units before values */
for (i = nf - 1; i >= 0; i--)
@@ -53,8 +361,10 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
switch (ftype[i])
{
case DTK_TIME:
- if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
- return -1;
+ dterr = DecodeTime(field[i], fmask, /* range, */
+ &tmask, tm, fsec);
+ if (dterr)
+ return dterr;
type = DTK_DAY;
break;
@@ -62,18 +372,19 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
/*
* Timezone is a token with a leading sign character and
- * otherwise the same as a non-signed time field
+ * at least one digit; there could be ':', '.', '-'
+ * embedded in it as well.
*/
+ /* Assert(*field[i] == '-' || *field[i] == '+'); */
/*
- * A single signed number ends up here, but will be rejected
- * by DecodeTime(). So, work this out to drop through to
- * DTK_NUMBER, which *can* tolerate this.
+ * Try for hh:mm or hh:mm:ss. If not, fall through to
+ * DTK_NUMBER case, which can handle signed float numbers
+ * and signed year-month values.
*/
- cp = field[i] + 1;
- while (*cp != '\0' && *cp != ':' && *cp != '.')
- cp++;
- if (*cp == ':' && DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0)
+ if (strchr(field[i] + 1, ':') != NULL &&
+ DecodeTime(field[i] + 1, fmask, /* INTERVAL_FULL_RANGE, */
+ &tmask, tm, fsec) == 0)
{
if (*field[i] == '-')
{
@@ -93,47 +404,81 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
tmask = DTK_M(TZ);
break;
}
- else if (type == IGNORE_DTF)
+ /* FALL THROUGH */
+
+ case DTK_DATE:
+ case DTK_NUMBER:
+ if (type == IGNORE_DTF)
{
- if (*cp == '.')
+ /* use typmod to decide what rightmost field is */
+ switch (range)
{
- /*
- * Got a decimal point? Then assume some sort of
- * seconds specification
- */
- type = DTK_SECOND;
- }
- else if (*cp == '\0')
- {
- /*
- * Only a signed integer? Then must assume a
- * timezone-like usage
- */
- type = DTK_HOUR;
+ case INTERVAL_MASK(YEAR):
+ type = DTK_YEAR;
+ break;
+ case INTERVAL_MASK(MONTH):
+ case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
+ type = DTK_MONTH;
+ break;
+ case INTERVAL_MASK(DAY):
+ type = DTK_DAY;
+ break;
+ case INTERVAL_MASK(HOUR):
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
+ case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+ type = DTK_HOUR;
+ break;
+ case INTERVAL_MASK(MINUTE):
+ case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
+ type = DTK_MINUTE;
+ break;
+ case INTERVAL_MASK(SECOND):
+ case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+ case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+ type = DTK_SECOND;
+ break;
+ default:
+ type = DTK_SECOND;
+ break;
}
}
- /* DROP THROUGH */
- case DTK_DATE:
- case DTK_NUMBER:
- val = strtol(field[i], &cp, 10);
+ errno = 0;
+ val = strtoi(field[i], &cp, 10);
+ if (errno == ERANGE)
+ return DTERR_FIELD_OVERFLOW;
- if (type == IGNORE_DTF)
- type = DTK_SECOND;
+ if (*cp == '-')
+ {
+ /* SQL "years-months" syntax */
+ int val2;
- if (*cp == '.')
+ val2 = strtoi(cp + 1, &cp, 10);
+ if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
+ return DTERR_FIELD_OVERFLOW;
+ if (*cp != '\0')
+ return DTERR_BAD_FORMAT;
+ type = DTK_MONTH;
+ if (*field[i] == '-')
+ val2 = -val2;
+ val = val * MONTHS_PER_YEAR + val2;
+ fval = 0;
+ }
+ else if (*cp == '.')
{
+ errno = 0;
fval = strtod(cp, &cp);
- if (*cp != '\0')
- return -1;
+ if (*cp != '\0' || errno != 0)
+ return DTERR_BAD_FORMAT;
- if (val < 0)
+ if (*field[i] == '-')
fval = -fval;
}
else if (*cp == '\0')
fval = 0;
else
- return -1;
+ return DTERR_BAD_FORMAT;
tmask = 0; /* DTK_M(type); */
@@ -141,135 +486,68 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
{
case DTK_MICROSEC:
#ifdef HAVE_INT64_TIMESTAMP
- *fsec += val + fval;
+ *fsec += rint(val + fval);
#else
*fsec += (val + fval) * 1e-6;
#endif
+ tmask = DTK_M(MICROSECOND);
break;
case DTK_MILLISEC:
#ifdef HAVE_INT64_TIMESTAMP
- *fsec += (val + fval) * 1000;
+ *fsec += rint((val + fval) * 1000);
#else
*fsec += (val + fval) * 1e-3;
#endif
+ tmask = DTK_M(MILLISECOND);
break;
case DTK_SECOND:
tm->tm_sec += val;
#ifdef HAVE_INT64_TIMESTAMP
- *fsec += fval * 1000000;
+ *fsec += rint(fval * 1000000);
#else
*fsec += fval;
#endif
- tmask = DTK_M(SECOND);
+
+ /*
+ * If any subseconds were specified, consider this
+ * microsecond and millisecond input as well.
+ */
+ if (fval == 0)
+ tmask = DTK_M(SECOND);
+ else
+ tmask = DTK_ALL_SECS_M;
break;
case DTK_MINUTE:
tm->tm_min += val;
- if (fval != 0)
- {
- int sec;
-
- fval *= SECS_PER_MINUTE;
- sec = fval;
- tm->tm_sec += sec;
-#ifdef HAVE_INT64_TIMESTAMP
- *fsec += ((fval - sec) * 1000000);
-#else
- *fsec += fval - sec;
-#endif
- }
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
tmask = DTK_M(MINUTE);
break;
case DTK_HOUR:
tm->tm_hour += val;
- if (fval != 0)
- {
- int sec;
-
- fval *= SECS_PER_HOUR;
- sec = fval;
- tm->tm_sec += sec;
-#ifdef HAVE_INT64_TIMESTAMP
- *fsec += (fval - sec) * 1000000;
-#else
- *fsec += fval - sec;
-#endif
- }
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
tmask = DTK_M(HOUR);
+ type = DTK_DAY;
break;
case DTK_DAY:
tm->tm_mday += val;
- if (fval != 0)
- {
- int sec;
-
- fval *= SECS_PER_DAY;
- sec = fval;
- tm->tm_sec += sec;
-#ifdef HAVE_INT64_TIMESTAMP
- *fsec += (fval - sec) * 1000000;
-#else
- *fsec += fval - sec;
-#endif
- }
+ AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
break;
case DTK_WEEK:
tm->tm_mday += val * 7;
- if (fval != 0)
- {
- int extra_days;
-
- fval *= 7;
- extra_days = (int32) fval;
- tm->tm_mday += extra_days;
- fval -= extra_days;
- if (fval != 0)
- {
- int sec;
-
- fval *= SECS_PER_DAY;
- sec = fval;
- tm->tm_sec += sec;
-#ifdef HAVE_INT64_TIMESTAMP
- *fsec += (fval - sec) * 1000000;
-#else
- *fsec += fval - sec;
-#endif
- }
- }
+ AdjustFractDays(fval, tm, fsec, 7);
tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
break;
case DTK_MONTH:
tm->tm_mon += val;
- if (fval != 0)
- {
- int day;
-
- fval *= DAYS_PER_MONTH;
- day = fval;
- tm->tm_mday += day;
- fval -= day;
- if (fval != 0)
- {
- int sec;
-
- fval *= SECS_PER_DAY;
- sec = fval;
- tm->tm_sec += sec;
-#ifdef HAVE_INT64_TIMESTAMP
- *fsec += (fval - sec) * 1000000;
-#else
- *fsec += fval - sec;
-#endif
- }
- }
+ AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
tmask = DTK_M(MONTH);
break;
@@ -302,7 +580,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
break;
@@ -330,19 +608,24 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
break;
default:
- return -1;
+ return DTERR_BAD_FORMAT;
}
if (tmask & fmask)
- return -1;
+ return DTERR_BAD_FORMAT;
fmask |= tmask;
}
+ /* ensure that at least one time field has been found */
+ if (fmask == 0)
+ return DTERR_BAD_FORMAT;
+
+ /* ensure fractional seconds are fractional */
if (*fsec != 0)
{
int sec;
@@ -356,250 +639,344 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
tm->tm_sec += sec;
}
+ /*----------
+ * The SQL standard defines the interval literal
+ * '-1 1:00:00'
+ * to mean "negative 1 days and negative 1 hours", while Postgres
+ * traditionally treats this as meaning "negative 1 days and positive
+ * 1 hours". In SQL_STANDARD intervalstyle, we apply the leading sign
+ * to all fields if there are no other explicit signs.
+ *
+ * We leave the signs alone if there are additional explicit signs.
+ * This protects us against misinterpreting postgres-style dump output,
+ * since the postgres-style output code has always put an explicit sign on
+ * all fields following a negative field. But note that SQL-spec output
+ * is ambiguous and can be misinterpreted on load! (So it's best practice
+ * to dump in postgres style, not SQL style.)
+ *----------
+ */
+ if (IntervalStyle == INTSTYLE_SQL_STANDARD && *field[0] == '-')
+ {
+ /* Check for additional explicit signs */
+ bool more_signs = false;
+
+ for (i = 1; i < nf; i++)
+ {
+ if (*field[i] == '-' || *field[i] == '+')
+ {
+ more_signs = true;
+ break;
+ }
+ }
+
+ if (!more_signs)
+ {
+ /*
+ * Rather than re-determining which field was field[0], just
+ * force 'em all negative.
+ */
+ if (*fsec > 0)
+ *fsec = -(*fsec);
+ if (tm->tm_sec > 0)
+ tm->tm_sec = -tm->tm_sec;
+ if (tm->tm_min > 0)
+ tm->tm_min = -tm->tm_min;
+ if (tm->tm_hour > 0)
+ tm->tm_hour = -tm->tm_hour;
+ if (tm->tm_mday > 0)
+ tm->tm_mday = -tm->tm_mday;
+ if (tm->tm_mon > 0)
+ tm->tm_mon = -tm->tm_mon;
+ if (tm->tm_year > 0)
+ tm->tm_year = -tm->tm_year;
+ }
+ }
+
+ /* finally, AGO negates everything */
if (is_before)
{
*fsec = -(*fsec);
- tm->tm_sec = -(tm->tm_sec);
- tm->tm_min = -(tm->tm_min);
- tm->tm_hour = -(tm->tm_hour);
- tm->tm_mday = -(tm->tm_mday);
- tm->tm_mon = -(tm->tm_mon);
- tm->tm_year = -(tm->tm_year);
+ tm->tm_sec = -tm->tm_sec;
+ tm->tm_min = -tm->tm_min;
+ tm->tm_hour = -tm->tm_hour;
+ tm->tm_mday = -tm->tm_mday;
+ tm->tm_mon = -tm->tm_mon;
+ tm->tm_year = -tm->tm_year;
}
- /* ensure that at least one time field has been found */
- return (fmask != 0) ? 0 : -1;
-} /* DecodeInterval() */
+ return 0;
+}
-/* EncodeInterval()
- * Interpret time structure as a delta time and convert to string.
- *
- * Support "traditional Postgres" and ISO-8601 styles.
- * Actually, afaik ISO does not address time interval formatting,
- * but this looks similar to the spec for absolute date/time.
- * - thomas 1998-04-30
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c */
+static char *
+AddVerboseIntPart(char *cp, int value, const char *units,
+ bool *is_zero, bool *is_before)
+{
+ if (value == 0)
+ return cp;
+ /* first nonzero value sets is_before */
+ if (*is_zero)
+ {
+ *is_before = (value < 0);
+ value = abs(value);
+ }
+ else if (*is_before)
+ value = -value;
+ sprintf(cp, " %d %s%s", value, units, (value == 1) ? "" : "s");
+ *is_zero = FALSE;
+ return cp + strlen(cp);
+}
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c */
+static char *
+AddPostgresIntPart(char *cp, int value, const char *units,
+ bool *is_zero, bool *is_before)
+{
+ if (value == 0)
+ return cp;
+ sprintf(cp, "%s%s%d %s%s",
+ (!*is_zero) ? " " : "",
+ (*is_before && value > 0) ? "+" : "",
+ value,
+ units,
+ (value != 1) ? "s" : "");
+ /*
+ * Each nonzero field sets is_before for (only) the next one. This is
+ * a tad bizarre but it's how it worked before...
+ */
+ *is_before = (value < 0);
+ *is_zero = FALSE;
+ return cp + strlen(cp);
+}
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c */
+static char *
+AddISO8601IntPart(char *cp, int value, char units)
+{
+ if (value == 0)
+ return cp;
+ sprintf(cp, "%d%c", value, units);
+ return cp + strlen(cp);
+}
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c */
+static void
+AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
+{
+ if (fsec == 0)
+ {
+ if (fillzeros)
+ sprintf(cp, "%02d", abs(sec));
+ else
+ sprintf(cp, "%d", abs(sec));
+ }
+ else
+ {
+#ifdef HAVE_INT64_TIMESTAMP
+ if (fillzeros)
+ sprintf(cp, "%02d.%0*d", abs(sec), precision, (int) Abs(fsec));
+ else
+ sprintf(cp, "%d.%0*d", abs(sec), precision, (int) Abs(fsec));
+#else
+ if (fillzeros)
+ sprintf(cp, "%0*.*f", precision + 3, precision, fabs(sec + fsec));
+ else
+ sprintf(cp, "%.*f", precision, fabs(sec + fsec));
+#endif
+ TrimTrailingZeros(cp);
+ }
+}
+
+
+/* copy&pasted from .../src/backend/utils/adt/datetime.c
+ *
+ * Change pg_tm to tm
*/
+
int
-EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
+EncodeInterval(struct /*pg_*/tm * tm, fsec_t fsec, int style, char *str)
{
- int is_before = FALSE;
- int is_nonzero = FALSE;
+
char *cp = str;
+ int year = tm->tm_year;
+ int mon = tm->tm_mon;
+ int mday = tm->tm_mday;
+ int hour = tm->tm_hour;
+ int min = tm->tm_min;
+ int sec = tm->tm_sec;
+ bool is_before = FALSE;
+ bool is_zero = TRUE;
/*
* The sign of year and month are guaranteed to match, since they are
* stored internally as "month". But we'll need to check for is_before and
- * is_nonzero when determining the signs of hour/minute/seconds fields.
+ * is_zero when determining the signs of day and hour/minute/seconds
+ * fields.
*/
switch (style)
{
- /* compatible with ISO date formats */
- case USE_ISO_DATES:
- if (tm->tm_year != 0)
- {
- sprintf(cp, "%d year%s",
- tm->tm_year, (tm->tm_year != 1) ? "s" : "");
- cp += strlen(cp);
- is_before = (tm->tm_year < 0);
- is_nonzero = TRUE;
- }
-
- if (tm->tm_mon != 0)
- {
- sprintf(cp, "%s%s%d mon%s", is_nonzero ? " " : "",
- (is_before && tm->tm_mon > 0) ? "+" : "",
- tm->tm_mon, (tm->tm_mon != 1) ? "s" : "");
- cp += strlen(cp);
- is_before = (tm->tm_mon < 0);
- is_nonzero = TRUE;
- }
-
- if (tm->tm_mday != 0)
- {
- sprintf(cp, "%s%s%d day%s", is_nonzero ? " " : "",
- (is_before && tm->tm_mday > 0) ? "+" : "",
- tm->tm_mday, (tm->tm_mday != 1) ? "s" : "");
- cp += strlen(cp);
- is_before = (tm->tm_mday < 0);
- is_nonzero = TRUE;
- }
- if (!is_nonzero || tm->tm_hour != 0 || tm->tm_min != 0 ||
- tm->tm_sec != 0 || fsec != 0)
+ /* SQL Standard interval format */
+ case INTSTYLE_SQL_STANDARD:
{
- int minus = tm->tm_hour < 0 || tm->tm_min < 0 ||
- tm->tm_sec < 0 || fsec < 0;
+ bool has_negative = year < 0 || mon < 0 ||
+ mday < 0 || hour < 0 ||
+ min < 0 || sec < 0 || fsec < 0;
+ bool has_positive = year > 0 || mon > 0 ||
+ mday > 0 || hour > 0 ||
+ min > 0 || sec > 0 || fsec > 0;
+ bool has_year_month = year != 0 || mon != 0;
+ bool has_day_time = mday != 0 || hour != 0 ||
+ min != 0 || sec != 0 || fsec != 0;
+ bool has_day = mday != 0;
+ bool sql_standard_value = !(has_negative && has_positive) &&
+ !(has_year_month && has_day_time);
- sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""),
- (minus ? "-" : (is_before ? "+" : "")),
- abs(tm->tm_hour), abs(tm->tm_min));
- cp += strlen(cp);
- /* Mark as "non-zero" since the fields are now filled in */
- is_nonzero = TRUE;
+ /*
+ * SQL Standard wants only 1 "<sign>" preceding the whole
+ * interval ... but can't do that if mixed signs.
+ */
+ if (has_negative && sql_standard_value)
+ {
+ *cp++ = '-';
+ year = -year;
+ mon = -mon;
+ mday = -mday;
+ hour = -hour;
+ min = -min;
+ sec = -sec;
+ fsec = -fsec;
+ }
- /* fractional seconds? */
- if (fsec != 0)
+ if (!has_negative && !has_positive)
{
-#ifdef HAVE_INT64_TIMESTAMP
- sprintf(cp, ":%02d", abs(tm->tm_sec));
+ sprintf(cp, "0");
+ }
+ else if (!sql_standard_value)
+ {
+ /*
+ * For non sql-standard interval values,
+ * force outputting the signs to avoid
+ * ambiguities with intervals with mixed
+ * sign components.
+ */
+ char year_sign = (year < 0 || mon < 0) ? '-' : '+';
+ char day_sign = (mday < 0) ? '-' : '+';
+ char sec_sign = (hour < 0 || min < 0 ||
+ sec < 0 || fsec < 0) ? '-' : '+';
+
+ sprintf(cp, "%c%d-%d %c%d %c%d:%02d:",
+ year_sign, abs(year), abs(mon),
+ day_sign, abs(mday),
+ sec_sign, abs(hour), abs(min));
cp += strlen(cp);
- sprintf(cp, ".%06d", Abs(fsec));
-#else
- fsec += tm->tm_sec;
- sprintf(cp, ":%012.9f", fabs(fsec));
-#endif
- TrimTrailingZeros(cp);
+ AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
+ }
+ else if (has_year_month)
+ {
+ sprintf(cp, "%d-%d", year, mon);
+ }
+ else if (has_day)
+ {
+ sprintf(cp, "%d %d:%02d:", mday, hour, min);
cp += strlen(cp);
- is_nonzero = TRUE;
+ AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
}
- /* otherwise, integer seconds only? */
- else if (tm->tm_sec != 0)
+ else
{
- sprintf(cp, ":%02d", abs(tm->tm_sec));
+ sprintf(cp, "%d:%02d:", hour, min);
cp += strlen(cp);
- is_nonzero = TRUE;
+ AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
}
}
break;
- case USE_POSTGRES_DATES:
- default:
- strcpy(cp, "@ ");
- cp += strlen(cp);
-
- if (tm->tm_year != 0)
+ /* ISO 8601 "time-intervals by duration only" */
+ case INTSTYLE_ISO_8601:
+ /* special-case zero to avoid printing nothing */
+ if (year == 0 && mon == 0 && mday == 0 &&
+ hour == 0 && min == 0 && sec == 0 && fsec == 0)
{
- int year = tm->tm_year;
-
- if (tm->tm_year < 0)
- year = -year;
-
- sprintf(cp, "%d year%s", year,
- (year != 1) ? "s" : "");
- cp += strlen(cp);
- is_before = (tm->tm_year < 0);
- is_nonzero = TRUE;
- }
-
- if (tm->tm_mon != 0)
- {
- int mon = tm->tm_mon;
-
- if (is_before || (!is_nonzero && tm->tm_mon < 0))
- mon = -mon;
-
- sprintf(cp, "%s%d mon%s", is_nonzero ? " " : "", mon,
- (mon != 1) ? "s" : "");
- cp += strlen(cp);
- if (!is_nonzero)
- is_before = (tm->tm_mon < 0);
- is_nonzero = TRUE;
- }
-
- if (tm->tm_mday != 0)
- {
- int day = tm->tm_mday;
-
- if (is_before || (!is_nonzero && tm->tm_mday < 0))
- day = -day;
-
- sprintf(cp, "%s%d day%s", is_nonzero ? " " : "", day,
- (day != 1) ? "s" : "");
- cp += strlen(cp);
- if (!is_nonzero)
- is_before = (tm->tm_mday < 0);
- is_nonzero = TRUE;
+ sprintf(cp, "PT0S");
+ break;
}
- if (tm->tm_hour != 0)
+ *cp++ = 'P';
+ cp = AddISO8601IntPart(cp, year, 'Y');
+ cp = AddISO8601IntPart(cp, mon , 'M');
+ cp = AddISO8601IntPart(cp, mday, 'D');
+ if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
+ *cp++ = 'T';
+ cp = AddISO8601IntPart(cp, hour, 'H');
+ cp = AddISO8601IntPart(cp, min , 'M');
+ if (sec != 0 || fsec != 0)
{
- int hour = tm->tm_hour;
-
- if (is_before || (!is_nonzero && tm->tm_hour < 0))
- hour = -hour;
-
- sprintf(cp, "%s%d hour%s", is_nonzero ? " " : "", hour,
- (hour != 1) ? "s" : "");
+ if (sec < 0 || fsec < 0)
+ *cp++ = '-';
+ AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
cp += strlen(cp);
- if (!is_nonzero)
- is_before = (tm->tm_hour < 0);
- is_nonzero = TRUE;
+ *cp++ = 'S';
+ *cp++ = '\0';
}
+ break;
- if (tm->tm_min != 0)
+ /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
+ case INTSTYLE_POSTGRES:
+ cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
+ cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
+ cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
+ if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
{
- int min = tm->tm_min;
+ bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
- if (is_before || (!is_nonzero && tm->tm_min < 0))
- min = -min;
-
- sprintf(cp, "%s%d min%s", is_nonzero ? " " : "", min,
- (min != 1) ? "s" : "");
+ sprintf(cp, "%s%s%02d:%02d:",
+ is_zero ? "" : " ",
+ (minus ? "-" : (is_before ? "+" : "")),
+ abs(hour), abs(min));
cp += strlen(cp);
- if (!is_nonzero)
- is_before = (tm->tm_min < 0);
- is_nonzero = TRUE;
+ AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
}
+ break;
- /* fractional seconds? */
- if (fsec != 0)
- {
-#ifdef HAVE_INT64_TIMESTAMP
- if (is_before || (!is_nonzero && tm->tm_sec < 0))
- tm->tm_sec = -tm->tm_sec;
- sprintf(cp, "%s%d.%02d secs", is_nonzero ? " " : "",
- tm->tm_sec, ((int) fsec) / 10000);
- cp += strlen(cp);
- if (!is_nonzero)
- is_before = (fsec < 0);
-#else
- fsec_t sec;
-
- fsec += tm->tm_sec;
- sec = fsec;
- if (is_before || (!is_nonzero && fsec < 0))
- sec = -sec;
-
- sprintf(cp, "%s%.2f secs", is_nonzero ? " " : "", sec);
- cp += strlen(cp);
- if (!is_nonzero)
- is_before = (fsec < 0);
-#endif
- is_nonzero = TRUE;
-
- /* otherwise, integer seconds only? */
- }
- else if (tm->tm_sec != 0)
+ /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
+ case INTSTYLE_POSTGRES_VERBOSE:
+ default:
+ strcpy(cp, "@");
+ cp++;
+ cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
+ cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
+ cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
+ cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
+ cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
+ if (sec != 0 || fsec != 0)
{
- int sec = tm->tm_sec;
-
- if (is_before || (!is_nonzero && tm->tm_sec < 0))
- sec = -sec;
-
- sprintf(cp, "%s%d sec%s", is_nonzero ? " " : "", sec,
- (sec != 1) ? "s" : "");
+ *cp++ = ' ';
+ if (sec < 0 || (sec == 0 && fsec < 0))
+ {
+ if (is_zero)
+ is_before = TRUE;
+ else if (!is_before)
+ *cp++ = '-';
+ }
+ else if (is_before)
+ *cp++ = '-';
+ AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
cp += strlen(cp);
- if (!is_nonzero)
- is_before = (tm->tm_sec < 0);
- is_nonzero = TRUE;
+ sprintf(cp, " sec%s",
+ (abs(sec) != 1 || fsec != 0) ? "s" : "");
+ is_zero = FALSE;
}
+ /* identically zero? then put in a unitless zero... */
+ if (is_zero)
+ strcat(cp, " 0");
+ if (is_before)
+ strcat(cp, " ago");
break;
}
- /* identically zero? then put in a unitless zero... */
- if (!is_nonzero)
- {
- strcat(cp, "0");
- cp += strlen(cp);
- }
-
- if (is_before && (style != USE_ISO_DATES))
- {
- strcat(cp, " ago");
- cp += strlen(cp);
- }
-
return 0;
} /* EncodeInterval() */
+
/* interval2tm()
* Convert a interval data type to a tm structure.
*/
@@ -719,7 +1096,8 @@ PGTYPESinterval_from_asc(char *str, char **endptr)
}
if (ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0 ||
- DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0)
+ (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0 &&
+ DecodeISO8601Interval(str, &dtype, tm, &fsec) != 0))
{
errno = PGTYPES_INTVL_BAD_INTERVAL;
return NULL;
@@ -754,7 +1132,7 @@ PGTYPESinterval_to_asc(interval * span)
*tm = &tt;
fsec_t fsec;
char buf[MAXDATELEN + 1];
- int DateStyle = 0;
+ int IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
if (interval2tm(*span, tm, &fsec) != 0)
{
@@ -762,7 +1140,7 @@ PGTYPESinterval_to_asc(interval * span)
return NULL;
}
- if (EncodeInterval(tm, fsec, DateStyle, buf) != 0)
+ if (EncodeInterval(tm, fsec, IntervalStyle, buf) != 0)
{
errno = PGTYPES_INTVL_BAD_INTERVAL;
return NULL;
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c
index 2dd263267d6..f71d3ab98d2 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c
@@ -77,6 +77,12 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
if (sqlca.sqlcode < 0) sqlprint ( );}
#line 30 "dt_test.pgc"
+ { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "set intervalstyle to postgres_verbose", ECPGt_EOIT, ECPGt_EORT);
+#line 31 "dt_test.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint ( );}
+#line 31 "dt_test.pgc"
+
date1 = PGTYPESdate_from_asc(d1, NULL);
ts1 = PGTYPEStimestamp_from_asc(t1, NULL);
@@ -86,10 +92,10 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
ECPGt_timestamp,&(ts1),(long)1,(long)1,sizeof(timestamp),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
-#line 35 "dt_test.pgc"
+#line 36 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 35 "dt_test.pgc"
+#line 36 "dt_test.pgc"
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select * from date_test where d = $1 ",
@@ -99,10 +105,10 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
ECPGt_timestamp,&(ts1),(long)1,(long)1,sizeof(timestamp),
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
-#line 37 "dt_test.pgc"
+#line 38 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 37 "dt_test.pgc"
+#line 38 "dt_test.pgc"
text = PGTYPESdate_to_asc(date1);
@@ -417,16 +423,16 @@ if (sqlca.sqlcode < 0) sqlprint ( );}
free(text);
{ ECPGtrans(__LINE__, NULL, "rollback ");
-#line 350 "dt_test.pgc"
+#line 351 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 350 "dt_test.pgc"
+#line 351 "dt_test.pgc"
{ ECPGdisconnect(__LINE__, "CURRENT");
-#line 351 "dt_test.pgc"
+#line 352 "dt_test.pgc"
if (sqlca.sqlcode < 0) sqlprint ( );}
-#line 351 "dt_test.pgc"
+#line 352 "dt_test.pgc"
return (0);
diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr
index 7dced6a7461..3aaee055cee 100644
--- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr
+++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr
@@ -14,29 +14,35 @@
[NO_PID]: sqlca: code: 0, state: 00000
[NO_PID]: ecpg_execute on line 30: OK: SET
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 35: query: insert into date_test ( d , ts ) values ( $1 , $2 ) ; with 2 parameter(s) on connection regress1
+[NO_PID]: ecpg_execute on line 31: query: set intervalstyle to postgres_verbose; with 0 parameter(s) on connection regress1
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 35: using PQexecParams
+[NO_PID]: ecpg_execute on line 31: using PQexec
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: free_params on line 35: parameter 1 = 1966-01-17
+[NO_PID]: ecpg_execute on line 31: OK: SET
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: free_params on line 35: parameter 2 = 2000-07-12 17:34:29
+[NO_PID]: ecpg_execute on line 36: query: insert into date_test ( d , ts ) values ( $1 , $2 ) ; with 2 parameter(s) on connection regress1
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 35: OK: INSERT 0 1
+[NO_PID]: ecpg_execute on line 36: using PQexecParams
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 37: query: select * from date_test where d = $1 ; with 1 parameter(s) on connection regress1
+[NO_PID]: free_params on line 36: parameter 1 = 1966-01-17
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 37: using PQexecParams
+[NO_PID]: free_params on line 36: parameter 2 = 2000-07-12 17:34:29
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: free_params on line 37: parameter 1 = 1966-01-17
+[NO_PID]: ecpg_execute on line 36: OK: INSERT 0 1
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 37: correctly got 1 tuples with 2 fields
+[NO_PID]: ecpg_execute on line 38: query: select * from date_test where d = $1 ; with 1 parameter(s) on connection regress1
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 37: RESULT: 1966-01-17 offset: -1; array: yes
+[NO_PID]: ecpg_execute on line 38: using PQexecParams
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 37: RESULT: 2000-07-12 17:34:29 offset: -1; array: yes
+[NO_PID]: free_params on line 38: parameter 1 = 1966-01-17
[NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ECPGtrans on line 350: action "rollback "; connection "regress1"
+[NO_PID]: ecpg_execute on line 38: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 38: RESULT: 1966-01-17 offset: -1; array: yes
+[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: yes
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGtrans on line 351: 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
diff --git a/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc b/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc
index d6c166477fc..fcf39cecc7c 100644
--- a/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc
+++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc
@@ -28,6 +28,7 @@ main(void)
exec sql connect to REGRESSDB1;
exec sql create table date_test (d date, ts timestamp);
exec sql set datestyle to iso;
+ exec sql set intervalstyle to postgres_verbose;
date1 = PGTYPESdate_from_asc(d1, NULL);
ts1 = PGTYPEStimestamp_from_asc(t1, NULL);