*** a/src/interfaces/ecpg/pgtypeslib/dt.h --- b/src/interfaces/ecpg/pgtypeslib/dt.h *************** *** 25,30 **** typedef double fsec_t; --- 25,46 ---- #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,82 **** typedef double fsec_t; --- 93,101 ---- * 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,111 **** 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 /* these are only for relative dates */ ! #define AGO 17 ! #define ABS_BEFORE 18 ! #define ABS_AFTER 19 /* generic fields to help with parsing */ ! #define ISODATE 20 ! #define ISOTIME 21 /* 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. --- 111,133 ---- #define HOUR 10 #define MINUTE 11 #define SECOND 12 ! #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 19 ! #define ABS_BEFORE 20 ! #define ABS_AFTER 21 /* generic fields to help with parsing */ ! #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,176 **** typedef double fsec_t; /* * Bit mask definitions for time parsing. */ ! #define DTK_M(t) (0x01 << (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 51 /* maximum possible length of an input date * string (not counting tr. null) */ #define MAXDATEFIELDS 25 /* maximum possible number of fields in a date * string */ --- 186,198 ---- /* * 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 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 */ *** a/src/interfaces/ecpg/pgtypeslib/interval.c --- b/src/interfaces/ecpg/pgtypeslib/interval.c *************** *** 13,51 **** #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. * ! * Allow "date" field DTK_DATE since this could be just ! * an unsigned floating point number. - thomas 1997-11-16 * ! * Allow ISO-style time span, with implicit units on number of days ! * preceding an hh:mm:ss field. - thomas 1998-04-30 */ int ! DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec) { ! int is_before = FALSE; ! char *cp; int fmask = 0, tmask, type; int i; 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; /* read through list backwards to pick up units before values */ for (i = nf - 1; i >= 0; i--) --- 13,359 ---- #include "pgtypes_error.h" #include "pgtypes_interval.h" ! /* 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; * ! * * ECPG semes not to have a global IntervalStyle ! * so added ! * int IntervalStyle = INTSTYLE_POSTGRES; * ! * * Assert wasn't available so removed it. */ int ! DecodeInterval(char **field, int *ftype, int nf, /*int range,*/ ! int *dtype, struct /*pg_*/tm * tm, fsec_t *fsec) { ! 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; ! ClearPgTm(tm,fsec); /* read through list backwards to pick up units before values */ for (i = nf - 1; i >= 0; i--) *************** *** 53,60 **** 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; type = DTK_DAY; break; --- 361,370 ---- switch (ftype[i]) { case DTK_TIME: ! dterr = DecodeTime(field[i], fmask, /* range, */ ! &tmask, tm, fsec); ! if (dterr) ! return dterr; type = DTK_DAY; break; *************** *** 62,79 **** 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 */ /* ! * 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. */ ! cp = field[i] + 1; ! while (*cp != '\0' && *cp != ':' && *cp != '.') ! cp++; ! if (*cp == ':' && DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0) { if (*field[i] == '-') { --- 372,390 ---- /* * Timezone is a token with a leading sign character and ! * at least one digit; there could be ':', '.', '-' ! * embedded in it as well. */ + /* Assert(*field[i] == '-' || *field[i] == '+'); */ /* ! * 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. */ ! if (strchr(field[i] + 1, ':') != NULL && ! DecodeTime(field[i] + 1, fmask, /* INTERVAL_FULL_RANGE, */ ! &tmask, tm, fsec) == 0) { if (*field[i] == '-') { *************** *** 93,139 **** DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse tmask = DTK_M(TZ); break; } ! else if (type == IGNORE_DTF) { ! if (*cp == '.') { ! /* ! * 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; } } - /* DROP THROUGH */ ! case DTK_DATE: ! case DTK_NUMBER: ! val = strtol(field[i], &cp, 10); ! if (type == IGNORE_DTF) ! type = DTK_SECOND; ! if (*cp == '.') { fval = strtod(cp, &cp); ! if (*cp != '\0') ! return -1; ! if (val < 0) fval = -fval; } else if (*cp == '\0') fval = 0; else ! return -1; tmask = 0; /* DTK_M(type); */ --- 404,484 ---- tmask = DTK_M(TZ); break; } ! /* FALL THROUGH */ ! ! case DTK_DATE: ! case DTK_NUMBER: ! if (type == IGNORE_DTF) { ! /* use typmod to decide what rightmost field is */ ! switch (range) { ! 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; } } ! errno = 0; ! val = strtoi(field[i], &cp, 10); ! if (errno == ERANGE) ! return DTERR_FIELD_OVERFLOW; ! if (*cp == '-') ! { ! /* SQL "years-months" syntax */ ! int val2; ! 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' || errno != 0) ! return DTERR_BAD_FORMAT; ! if (*field[i] == '-') fval = -fval; } else if (*cp == '\0') fval = 0; else ! return DTERR_BAD_FORMAT; tmask = 0; /* DTK_M(type); */ *************** *** 141,275 **** DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse { case DTK_MICROSEC: #ifdef HAVE_INT64_TIMESTAMP ! *fsec += val + fval; #else *fsec += (val + fval) * 1e-6; #endif break; case DTK_MILLISEC: #ifdef HAVE_INT64_TIMESTAMP ! *fsec += (val + fval) * 1000; #else *fsec += (val + fval) * 1e-3; #endif break; case DTK_SECOND: tm->tm_sec += val; #ifdef HAVE_INT64_TIMESTAMP ! *fsec += fval * 1000000; #else *fsec += fval; #endif ! tmask = DTK_M(SECOND); 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 ! } 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 ! } tmask = DTK_M(HOUR); 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 ! } 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 ! } ! } 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 ! } ! } tmask = DTK_M(MONTH); break; --- 486,553 ---- { case DTK_MICROSEC: #ifdef HAVE_INT64_TIMESTAMP ! *fsec += rint(val + fval); #else *fsec += (val + fval) * 1e-6; #endif + tmask = DTK_M(MICROSECOND); break; case DTK_MILLISEC: #ifdef HAVE_INT64_TIMESTAMP ! *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 += rint(fval * 1000000); #else *fsec += fval; #endif ! ! /* ! * 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; ! AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE); tmask = DTK_M(MINUTE); break; case DTK_HOUR: tm->tm_hour += val; ! AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR); tmask = DTK_M(HOUR); + type = DTK_DAY; break; case DTK_DAY: tm->tm_mday += val; ! 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; ! AdjustFractDays(fval, tm, fsec, 7); tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY); break; case DTK_MONTH: tm->tm_mon += val; ! AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH); tmask = DTK_M(MONTH); break; *************** *** 302,308 **** DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse break; default: ! return -1; } break; --- 580,586 ---- break; default: ! return DTERR_BAD_FORMAT; } break; *************** *** 330,348 **** DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse break; default: ! return -1; } break; default: ! return -1; } if (tmask & fmask) ! return -1; fmask |= tmask; } if (*fsec != 0) { int sec; --- 608,631 ---- break; default: ! return DTERR_BAD_FORMAT; } break; default: ! return DTERR_BAD_FORMAT; } if (tmask & fmask) ! 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,605 **** DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse tm->tm_sec += sec; } 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); } ! /* ensure that at least one time field has been found */ ! return (fmask != 0) ? 0 : -1; ! } /* DecodeInterval() */ ! /* 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 */ int ! EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str) { ! int is_before = FALSE; ! int is_nonzero = FALSE; char *cp = str; /* * 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. */ 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) { ! int minus = tm->tm_hour < 0 || tm->tm_min < 0 || ! tm->tm_sec < 0 || fsec < 0; ! 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; ! /* fractional seconds? */ ! if (fsec != 0) { ! #ifdef HAVE_INT64_TIMESTAMP ! sprintf(cp, ":%02d", abs(tm->tm_sec)); cp += strlen(cp); ! sprintf(cp, ".%06d", Abs(fsec)); ! #else ! fsec += tm->tm_sec; ! sprintf(cp, ":%012.9f", fabs(fsec)); ! #endif ! TrimTrailingZeros(cp); cp += strlen(cp); ! is_nonzero = TRUE; } ! /* otherwise, integer seconds only? */ ! else if (tm->tm_sec != 0) { ! sprintf(cp, ":%02d", abs(tm->tm_sec)); cp += strlen(cp); ! is_nonzero = TRUE; } } break; ! case USE_POSTGRES_DATES: ! default: ! strcpy(cp, "@ "); ! cp += strlen(cp); ! ! if (tm->tm_year != 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; } ! if (tm->tm_hour != 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" : ""); cp += strlen(cp); ! if (!is_nonzero) ! is_before = (tm->tm_hour < 0); ! is_nonzero = TRUE; } ! if (tm->tm_min != 0) { ! int min = tm->tm_min; ! if (is_before || (!is_nonzero && tm->tm_min < 0)) ! min = -min; ! ! sprintf(cp, "%s%d min%s", is_nonzero ? " " : "", min, ! (min != 1) ? "s" : ""); cp += strlen(cp); ! if (!is_nonzero) ! is_before = (tm->tm_min < 0); ! is_nonzero = TRUE; } ! /* 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) { ! 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 += strlen(cp); ! if (!is_nonzero) ! is_before = (tm->tm_sec < 0); ! is_nonzero = TRUE; } 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. */ --- 639,982 ---- 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; } ! return 0; ! } ! ! /* 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 /*pg_*/tm * tm, fsec_t fsec, int style, char *str) { ! 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_zero when determining the signs of day and hour/minute/seconds ! * fields. */ switch (style) { ! /* SQL Standard interval format */ ! case INTSTYLE_SQL_STANDARD: { ! 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); ! /* ! * SQL Standard wants only 1 "" 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; ! } ! if (!has_negative && !has_positive) { ! 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); ! 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); ! AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); } ! else { ! sprintf(cp, "%d:%02d:", hour, min); cp += strlen(cp); ! AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); } } break; ! /* 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) { ! sprintf(cp, "PT0S"); ! break; } ! *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) { ! if (sec < 0 || fsec < 0) ! *cp++ = '-'; ! AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false); cp += strlen(cp); ! *cp++ = 'S'; ! *cp++ = '\0'; } + break; ! /* 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) { ! bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0); ! sprintf(cp, "%s%s%02d:%02d:", ! is_zero ? "" : " ", ! (minus ? "-" : (is_before ? "+" : "")), ! abs(hour), abs(min)); cp += strlen(cp); ! AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); } + break; ! /* 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) { ! *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); ! 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; } return 0; } /* EncodeInterval() */ + /* interval2tm() * Convert a interval data type to a tm structure. */ *************** *** 719,725 **** 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) { errno = PGTYPES_INTVL_BAD_INTERVAL; return NULL; --- 1096,1103 ---- } if (ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0 || ! (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0 && ! DecodeISO8601Interval(str, &dtype, tm, &fsec) != 0)) { errno = PGTYPES_INTVL_BAD_INTERVAL; return NULL; *************** *** 754,760 **** PGTYPESinterval_to_asc(interval * span) *tm = &tt; fsec_t fsec; char buf[MAXDATELEN + 1]; ! int DateStyle = 0; if (interval2tm(*span, tm, &fsec) != 0) { --- 1132,1138 ---- *tm = &tt; fsec_t fsec; char buf[MAXDATELEN + 1]; ! int IntervalStyle = INTSTYLE_POSTGRES_VERBOSE; if (interval2tm(*span, tm, &fsec) != 0) { *************** *** 762,768 **** PGTYPESinterval_to_asc(interval * span) return NULL; } ! if (EncodeInterval(tm, fsec, DateStyle, buf) != 0) { errno = PGTYPES_INTVL_BAD_INTERVAL; return NULL; --- 1140,1146 ---- return NULL; } ! if (EncodeInterval(tm, fsec, IntervalStyle, buf) != 0) { errno = PGTYPES_INTVL_BAD_INTERVAL; return NULL; *** a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c --- b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.c *************** *** 77,82 **** if (sqlca.sqlcode < 0) sqlprint ( );} --- 77,88 ---- 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,95 **** 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" if (sqlca.sqlcode < 0) sqlprint ( );} ! #line 35 "dt_test.pgc" { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select * from date_test where d = $1 ", --- 92,101 ---- 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 36 "dt_test.pgc" if (sqlca.sqlcode < 0) sqlprint ( );} ! #line 36 "dt_test.pgc" { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select * from date_test where d = $1 ", *************** *** 99,108 **** 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" if (sqlca.sqlcode < 0) sqlprint ( );} ! #line 37 "dt_test.pgc" text = PGTYPESdate_to_asc(date1); --- 105,114 ---- 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 38 "dt_test.pgc" if (sqlca.sqlcode < 0) sqlprint ( );} ! #line 38 "dt_test.pgc" text = PGTYPESdate_to_asc(date1); *************** *** 417,432 **** if (sqlca.sqlcode < 0) sqlprint ( );} free(text); { ECPGtrans(__LINE__, NULL, "rollback "); ! #line 350 "dt_test.pgc" if (sqlca.sqlcode < 0) sqlprint ( );} ! #line 350 "dt_test.pgc" { ECPGdisconnect(__LINE__, "CURRENT"); ! #line 351 "dt_test.pgc" if (sqlca.sqlcode < 0) sqlprint ( );} ! #line 351 "dt_test.pgc" return (0); --- 423,438 ---- free(text); { ECPGtrans(__LINE__, NULL, "rollback "); ! #line 351 "dt_test.pgc" if (sqlca.sqlcode < 0) sqlprint ( );} ! #line 351 "dt_test.pgc" { ECPGdisconnect(__LINE__, "CURRENT"); ! #line 352 "dt_test.pgc" if (sqlca.sqlcode < 0) sqlprint ( );} ! #line 352 "dt_test.pgc" return (0); *** a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr --- b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test.stderr *************** *** 14,42 **** [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]: sqlca: code: 0, state: 00000 ! [NO_PID]: ecpg_execute on line 35: using PQexecParams [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: free_params on line 35: parameter 1 = 1966-01-17 [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: free_params on line 35: parameter 2 = 2000-07-12 17:34:29 [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: ecpg_execute on line 35: OK: INSERT 0 1 [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]: sqlca: code: 0, state: 00000 ! [NO_PID]: ecpg_execute on line 37: using PQexecParams [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: free_params on line 37: parameter 1 = 1966-01-17 [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: ecpg_execute on line 37: correctly got 1 tuples with 2 fields [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]: 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]: sqlca: code: 0, state: 00000 ! [NO_PID]: ECPGtrans on line 350: 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 --- 14,48 ---- [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 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 31: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: ecpg_execute on line 31: OK: SET [NO_PID]: sqlca: code: 0, state: 00000 ! [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 36: using PQexecParams [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: free_params on line 36: parameter 1 = 1966-01-17 [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: free_params on line 36: parameter 2 = 2000-07-12 17:34:29 [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: ecpg_execute on line 36: OK: INSERT 0 1 [NO_PID]: sqlca: code: 0, state: 00000 ! [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_execute on line 38: using PQexecParams [NO_PID]: sqlca: code: 0, state: 00000 ! [NO_PID]: free_params on line 38: parameter 1 = 1966-01-17 [NO_PID]: sqlca: code: 0, state: 00000 ! [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 *** a/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc --- b/src/interfaces/ecpg/test/pgtypeslib/dt_test.pgc *************** *** 28,33 **** main(void) --- 28,34 ---- 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);