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 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); 
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to