Re: Re: Updated interval patches - ECPG [was, intervalstyle....] - Mailing list pgsql-hackers

From Ron Mayer
Subject Re: Re: Updated interval patches - ECPG [was, intervalstyle....]
Date
Msg-id 492609DC.5070608@cheapcomplexdevices.com
Whole thread Raw
In response to Re: Re: Updated interval patches - ECPG [was, intervalstyle....]  (Michael Meskes <meskes@postgresql.org>)
Responses Re: Re: Updated interval patches - ECPG [was, intervalstyle....]
List pgsql-hackers
Michael Meskes wrote:
> On Wed, Nov 12, 2008 at 02:28:56PM -0800, Ron Mayer wrote:
>> Merging of the interval style into ecpg attached.
> Thanks for caring about the ecpg changes too.

Thanks for the comments.  Updated the patch.

>> I know little enough about ecpg that I can't really tell if these changes
>> are for the better or worse.
> The closer pgtypeslib is to the backend the better.
>
>> One thing in the patch that's probably a bug is that the
>> constants in src/include/utils/dt.h and src/include/utils/datetime.h
>> under the section "Fields for time decoding" seem not to match, so
> Assuming you mean src/interfaces/ecpg/pgtypeslib/dt.h. The numbers should match IMO.

Ok.  I copy&pasted them from datetime.h to dt.h.
This changes a number of values that were like
#define DOY        13
#define DOW        14
to
#define DOY        15
#define DOW        16
and I'm not quite sure what the consequences of that might be,
but the regression tests still pass.

> Also one files seems to be missing, there are no changes to
> test/expected/pgtypeslib-dt_test.c in the patch, but when changing dt_test.pgc
> this file should be changed too.
>
> Could you add this to your work too?

Got it.
Patch attached.


*** 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 "<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;
!                 }

!                 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
connectionregress1 
  [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
connectionregress1 
  [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);

pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: Proposed Patch to Improve Performance of Multi-Batch Hash Join for Skewed Data Sets
Next
From: KaiGai Kohei
Date:
Subject: Re: Updates of SE-PostgreSQL 8.4devel patches (r1197)