LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - interval.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 33.3 % 604 201
Test Date: 2026-07-25 22:15:46 Functions: 73.7 % 19 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 15.9 % 416 66

             Branch data     Line data    Source code
       1                 :             : /* src/interfaces/ecpg/pgtypeslib/interval.c */
       2                 :             : 
       3                 :             : #include "postgres_fe.h"
       4                 :             : 
       5                 :             : #include <time.h>
       6                 :             : #include <math.h>
       7                 :             : #include <limits.h>
       8                 :             : 
       9                 :             : #include "common/string.h"
      10                 :             : #include "dt.h"
      11                 :             : #include "pgtypes_error.h"
      12                 :             : #include "pgtypes_interval.h"
      13                 :             : #include "pgtypeslib_extern.h"
      14                 :             : 
      15                 :             : /*
      16                 :             :  * copy&pasted from .../src/backend/utils/adt/datetime.c
      17                 :             :  * and changed struct pg_tm to struct tm
      18                 :             :  */
      19                 :             : static void
      20                 :          76 : AdjustFractSeconds(double frac, struct /* pg_ */ tm *tm, fsec_t *fsec, int scale)
      21                 :             : {
      22                 :             :     int         sec;
      23                 :             : 
      24         [ +  - ]:          76 :     if (frac == 0)
      25                 :          76 :         return;
      26                 :           0 :     frac *= scale;
      27                 :           0 :     sec = (int) frac;
      28                 :           0 :     tm->tm_sec += sec;
      29                 :           0 :     frac -= sec;
      30                 :           0 :     *fsec += rint(frac * 1000000);
      31                 :             : }
      32                 :             : 
      33                 :             : 
      34                 :             : /*
      35                 :             :  * copy&pasted from .../src/backend/utils/adt/datetime.c
      36                 :             :  * and changed struct pg_tm to struct tm
      37                 :             :  */
      38                 :             : static void
      39                 :           0 : AdjustFractDays(double frac, struct /* pg_ */ tm *tm, fsec_t *fsec, int scale)
      40                 :             : {
      41                 :             :     int         extra_days;
      42                 :             : 
      43         [ #  # ]:           0 :     if (frac == 0)
      44                 :           0 :         return;
      45                 :           0 :     frac *= scale;
      46                 :           0 :     extra_days = (int) frac;
      47                 :           0 :     tm->tm_mday += extra_days;
      48                 :           0 :     frac -= extra_days;
      49                 :           0 :     AdjustFractSeconds(frac, tm, fsec, SECS_PER_DAY);
      50                 :             : }
      51                 :             : 
      52                 :             : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
      53                 :             : static int
      54                 :           0 : ParseISO8601Number(const char *str, char **endptr, int *ipart, double *fpart)
      55                 :             : {
      56                 :             :     double      val;
      57                 :             : 
      58   [ #  #  #  #  :           0 :     if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
                   #  # ]
      59                 :           0 :         return DTERR_BAD_FORMAT;
      60                 :           0 :     errno = 0;
      61                 :           0 :     val = strtod(str, endptr);
      62                 :             :     /* did we not see anything that looks like a double? */
      63   [ #  #  #  # ]:           0 :     if (*endptr == str || errno != 0)
      64                 :           0 :         return DTERR_BAD_FORMAT;
      65                 :             :     /* watch out for overflow */
      66   [ #  #  #  # ]:           0 :     if (val < INT_MIN || val > INT_MAX)
      67                 :           0 :         return DTERR_FIELD_OVERFLOW;
      68                 :             :     /* be very sure we truncate towards zero (cf dtrunc()) */
      69         [ #  # ]:           0 :     if (val >= 0)
      70                 :           0 :         *ipart = (int) floor(val);
      71                 :             :     else
      72                 :           0 :         *ipart = (int) -floor(-val);
      73                 :           0 :     *fpart = val - *ipart;
      74                 :           0 :     return 0;
      75                 :             : }
      76                 :             : 
      77                 :             : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
      78                 :             : static int
      79                 :           0 : ISO8601IntegerWidth(const char *fieldstart)
      80                 :             : {
      81                 :             :     /* We might have had a leading '-' */
      82         [ #  # ]:           0 :     if (*fieldstart == '-')
      83                 :           0 :         fieldstart++;
      84                 :           0 :     return strspn(fieldstart, "0123456789");
      85                 :             : }
      86                 :             : 
      87                 :             : 
      88                 :             : /*
      89                 :             :  * copy&pasted from .../src/backend/utils/adt/datetime.c
      90                 :             :  * and changed struct pg_tm to struct tm
      91                 :             :  */
      92                 :             : static inline void
      93                 :          60 : ClearPgTm(struct /* pg_ */ tm *tm, fsec_t *fsec)
      94                 :             : {
      95                 :          60 :     tm->tm_year = 0;
      96                 :          60 :     tm->tm_mon = 0;
      97                 :          60 :     tm->tm_mday = 0;
      98                 :          60 :     tm->tm_hour = 0;
      99                 :          60 :     tm->tm_min = 0;
     100                 :          60 :     tm->tm_sec = 0;
     101                 :          60 :     *fsec = 0;
     102                 :          60 : }
     103                 :             : 
     104                 :             : /*
     105                 :             :  * copy&pasted from .../src/backend/utils/adt/datetime.c
     106                 :             :  *
     107                 :             :  * * changed struct pg_tm to struct tm
     108                 :             :  *
     109                 :             :  * * Made the function static
     110                 :             :  */
     111                 :             : static int
     112                 :           2 : DecodeISO8601Interval(char *str,
     113                 :             :                       int *dtype, struct /* pg_ */ tm *tm, fsec_t *fsec)
     114                 :             : {
     115                 :           2 :     bool        datepart = true;
     116                 :           2 :     bool        havefield = false;
     117                 :             : 
     118                 :           2 :     *dtype = DTK_DELTA;
     119                 :           2 :     ClearPgTm(tm, fsec);
     120                 :             : 
     121   [ +  -  +  - ]:           2 :     if (strlen(str) < 2 || str[0] != 'P')
     122                 :           2 :         return DTERR_BAD_FORMAT;
     123                 :             : 
     124                 :           0 :     str++;
     125         [ #  # ]:           0 :     while (*str)
     126                 :             :     {
     127                 :             :         char       *fieldstart;
     128                 :             :         int         val;
     129                 :             :         double      fval;
     130                 :             :         char        unit;
     131                 :             :         int         dterr;
     132                 :             : 
     133         [ #  # ]:           0 :         if (*str == 'T')        /* T indicates the beginning of the time part */
     134                 :             :         {
     135                 :           0 :             datepart = false;
     136                 :           0 :             havefield = false;
     137                 :           0 :             str++;
     138                 :           0 :             continue;
     139                 :             :         }
     140                 :             : 
     141                 :           0 :         fieldstart = str;
     142                 :           0 :         dterr = ParseISO8601Number(str, &str, &val, &fval);
     143         [ #  # ]:           0 :         if (dterr)
     144                 :           0 :             return dterr;
     145                 :             : 
     146                 :             :         /*
     147                 :             :          * Note: we could step off the end of the string here.  Code below
     148                 :             :          * *must* exit the loop if unit == '\0'.
     149                 :             :          */
     150                 :           0 :         unit = *str++;
     151                 :             : 
     152         [ #  # ]:           0 :         if (datepart)
     153                 :             :         {
     154   [ #  #  #  #  :           0 :             switch (unit)       /* before T: Y M W D */
                #  #  # ]
     155                 :             :             {
     156                 :           0 :                 case 'Y':
     157                 :           0 :                     tm->tm_year += val;
     158                 :           0 :                     tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
     159                 :           0 :                     break;
     160                 :           0 :                 case 'M':
     161                 :           0 :                     tm->tm_mon += val;
     162                 :           0 :                     AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
     163                 :           0 :                     break;
     164                 :           0 :                 case 'W':
     165                 :           0 :                     tm->tm_mday += val * 7;
     166                 :           0 :                     AdjustFractDays(fval, tm, fsec, 7);
     167                 :           0 :                     break;
     168                 :           0 :                 case 'D':
     169                 :           0 :                     tm->tm_mday += val;
     170                 :           0 :                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
     171                 :           0 :                     break;
     172                 :           0 :                 case 'T':       /* ISO 8601 4.4.3.3 Alternative Format / Basic */
     173                 :             :                 case '\0':
     174   [ #  #  #  # ]:           0 :                     if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
     175                 :             :                     {
     176                 :           0 :                         tm->tm_year += val / 10000;
     177                 :           0 :                         tm->tm_mon += (val / 100) % 100;
     178                 :           0 :                         tm->tm_mday += val % 100;
     179                 :           0 :                         AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
     180         [ #  # ]:           0 :                         if (unit == '\0')
     181                 :           0 :                             return 0;
     182                 :           0 :                         datepart = false;
     183                 :           0 :                         havefield = false;
     184                 :           0 :                         continue;
     185                 :             :                     }
     186                 :             :                     /* Else fall through to extended alternative format */
     187                 :             :                     pg_fallthrough;
     188                 :             :                 case '-':       /* ISO 8601 4.4.3.3 Alternative Format,
     189                 :             :                                  * Extended */
     190         [ #  # ]:           0 :                     if (havefield)
     191                 :           0 :                         return DTERR_BAD_FORMAT;
     192                 :             : 
     193                 :           0 :                     tm->tm_year += val;
     194                 :           0 :                     tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
     195         [ #  # ]:           0 :                     if (unit == '\0')
     196                 :           0 :                         return 0;
     197         [ #  # ]:           0 :                     if (unit == 'T')
     198                 :             :                     {
     199                 :           0 :                         datepart = false;
     200                 :           0 :                         havefield = false;
     201                 :           0 :                         continue;
     202                 :             :                     }
     203                 :             : 
     204                 :           0 :                     dterr = ParseISO8601Number(str, &str, &val, &fval);
     205         [ #  # ]:           0 :                     if (dterr)
     206                 :           0 :                         return dterr;
     207                 :           0 :                     tm->tm_mon += val;
     208                 :           0 :                     AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
     209         [ #  # ]:           0 :                     if (*str == '\0')
     210                 :           0 :                         return 0;
     211         [ #  # ]:           0 :                     if (*str == 'T')
     212                 :             :                     {
     213                 :           0 :                         datepart = false;
     214                 :           0 :                         havefield = false;
     215                 :           0 :                         continue;
     216                 :             :                     }
     217         [ #  # ]:           0 :                     if (*str != '-')
     218                 :           0 :                         return DTERR_BAD_FORMAT;
     219                 :           0 :                     str++;
     220                 :             : 
     221                 :           0 :                     dterr = ParseISO8601Number(str, &str, &val, &fval);
     222         [ #  # ]:           0 :                     if (dterr)
     223                 :           0 :                         return dterr;
     224                 :           0 :                     tm->tm_mday += val;
     225                 :           0 :                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
     226         [ #  # ]:           0 :                     if (*str == '\0')
     227                 :           0 :                         return 0;
     228         [ #  # ]:           0 :                     if (*str == 'T')
     229                 :             :                     {
     230                 :           0 :                         datepart = false;
     231                 :           0 :                         havefield = false;
     232                 :           0 :                         continue;
     233                 :             :                     }
     234                 :           0 :                     return DTERR_BAD_FORMAT;
     235                 :           0 :                 default:
     236                 :             :                     /* not a valid date unit suffix */
     237                 :           0 :                     return DTERR_BAD_FORMAT;
     238                 :             :             }
     239                 :             :         }
     240                 :             :         else
     241                 :             :         {
     242   [ #  #  #  #  :           0 :             switch (unit)       /* after T: H M S */
                   #  # ]
     243                 :             :             {
     244                 :           0 :                 case 'H':
     245                 :           0 :                     tm->tm_hour += val;
     246                 :           0 :                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
     247                 :           0 :                     break;
     248                 :           0 :                 case 'M':
     249                 :           0 :                     tm->tm_min += val;
     250                 :           0 :                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
     251                 :           0 :                     break;
     252                 :           0 :                 case 'S':
     253                 :           0 :                     tm->tm_sec += val;
     254                 :           0 :                     AdjustFractSeconds(fval, tm, fsec, 1);
     255                 :           0 :                     break;
     256                 :           0 :                 case '\0':      /* ISO 8601 4.4.3.3 Alternative Format */
     257   [ #  #  #  # ]:           0 :                     if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
     258                 :             :                     {
     259                 :           0 :                         tm->tm_hour += val / 10000;
     260                 :           0 :                         tm->tm_min += (val / 100) % 100;
     261                 :           0 :                         tm->tm_sec += val % 100;
     262                 :           0 :                         AdjustFractSeconds(fval, tm, fsec, 1);
     263                 :           0 :                         return 0;
     264                 :             :                     }
     265                 :             :                     /* Else fall through to extended alternative format */
     266                 :             :                     pg_fallthrough;
     267                 :             :                 case ':':       /* ISO 8601 4.4.3.3 Alternative Format,
     268                 :             :                                  * Extended */
     269         [ #  # ]:           0 :                     if (havefield)
     270                 :           0 :                         return DTERR_BAD_FORMAT;
     271                 :             : 
     272                 :           0 :                     tm->tm_hour += val;
     273                 :           0 :                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
     274         [ #  # ]:           0 :                     if (unit == '\0')
     275                 :           0 :                         return 0;
     276                 :             : 
     277                 :           0 :                     dterr = ParseISO8601Number(str, &str, &val, &fval);
     278         [ #  # ]:           0 :                     if (dterr)
     279                 :           0 :                         return dterr;
     280                 :           0 :                     tm->tm_min += val;
     281                 :           0 :                     AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
     282         [ #  # ]:           0 :                     if (*str == '\0')
     283                 :           0 :                         return 0;
     284         [ #  # ]:           0 :                     if (*str != ':')
     285                 :           0 :                         return DTERR_BAD_FORMAT;
     286                 :           0 :                     str++;
     287                 :             : 
     288                 :           0 :                     dterr = ParseISO8601Number(str, &str, &val, &fval);
     289         [ #  # ]:           0 :                     if (dterr)
     290                 :           0 :                         return dterr;
     291                 :           0 :                     tm->tm_sec += val;
     292                 :           0 :                     AdjustFractSeconds(fval, tm, fsec, 1);
     293         [ #  # ]:           0 :                     if (*str == '\0')
     294                 :           0 :                         return 0;
     295                 :           0 :                     return DTERR_BAD_FORMAT;
     296                 :             : 
     297                 :           0 :                 default:
     298                 :             :                     /* not a valid time unit suffix */
     299                 :           0 :                     return DTERR_BAD_FORMAT;
     300                 :             :             }
     301                 :             :         }
     302                 :             : 
     303                 :           0 :         havefield = true;
     304                 :             :     }
     305                 :             : 
     306                 :           0 :     return 0;
     307                 :             : }
     308                 :             : 
     309                 :             : 
     310                 :             : 
     311                 :             : /*
     312                 :             :  * copy&pasted from .../src/backend/utils/adt/datetime.c
     313                 :             :  * with 3 exceptions
     314                 :             :  *
     315                 :             :  *  * changed struct pg_tm to struct tm
     316                 :             :  *
     317                 :             :  *  * ECPG code called this without a 'range' parameter
     318                 :             :  *    removed 'int range' from the argument list and
     319                 :             :  *    places where DecodeTime is called; and added
     320                 :             :  *       int range = INTERVAL_FULL_RANGE;
     321                 :             :  *
     322                 :             :  *  * ECPG seems not to have a global IntervalStyle
     323                 :             :  *    so added
     324                 :             :  *      int IntervalStyle = INTSTYLE_POSTGRES;
     325                 :             :  */
     326                 :             : int
     327                 :          58 : DecodeInterval(char **field, int *ftype, int nf,    /* int range, */
     328                 :             :                int *dtype, struct /* pg_ */ tm *tm, fsec_t *fsec)
     329                 :             : {
     330                 :          58 :     int         IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
     331                 :          58 :     int         range = INTERVAL_FULL_RANGE;
     332                 :          58 :     bool        is_before = false;
     333                 :             :     char       *cp;
     334                 :          58 :     int         fmask = 0,
     335                 :             :                 tmask,
     336                 :             :                 type;
     337                 :             :     int         i;
     338                 :             :     int         dterr;
     339                 :             :     int         val;
     340                 :             :     double      fval;
     341                 :             : 
     342                 :          58 :     *dtype = DTK_DELTA;
     343                 :          58 :     type = IGNORE_DTF;
     344                 :          58 :     ClearPgTm(tm, fsec);
     345                 :             : 
     346                 :             :     /* read through list backwards to pick up units before values */
     347         [ +  + ]:         234 :     for (i = nf - 1; i >= 0; i--)
     348                 :             :     {
     349   [ +  -  +  +  :         178 :         switch (ftype[i])
                      - ]
     350                 :             :         {
     351                 :           2 :             case DTK_TIME:
     352                 :           2 :                 dterr = DecodeTime(field[i],    /* range, */
     353                 :             :                                    &tmask, tm, fsec);
     354         [ -  + ]:           2 :                 if (dterr)
     355                 :           0 :                     return dterr;
     356                 :           2 :                 type = DTK_DAY;
     357                 :           2 :                 break;
     358                 :             : 
     359                 :           0 :             case DTK_TZ:
     360                 :             : 
     361                 :             :                 /*
     362                 :             :                  * Timezone is a token with a leading sign character and at
     363                 :             :                  * least one digit; there could be ':', '.', '-' embedded in
     364                 :             :                  * it as well.
     365                 :             :                  */
     366                 :             :                 Assert(*field[i] == '-' || *field[i] == '+');
     367                 :             : 
     368                 :             :                 /*
     369                 :             :                  * Try for hh:mm or hh:mm:ss.  If not, fall through to
     370                 :             :                  * DTK_NUMBER case, which can handle signed float numbers and
     371                 :             :                  * signed year-month values.
     372                 :             :                  */
     373   [ #  #  #  # ]:           0 :                 if (strchr(field[i] + 1, ':') != NULL &&
     374                 :           0 :                     DecodeTime(field[i] + 1,    /* INTERVAL_FULL_RANGE, */
     375                 :             :                                &tmask, tm, fsec) == 0)
     376                 :             :                 {
     377         [ #  # ]:           0 :                     if (*field[i] == '-')
     378                 :             :                     {
     379                 :             :                         /* flip the sign on all fields */
     380                 :           0 :                         tm->tm_hour = -tm->tm_hour;
     381                 :           0 :                         tm->tm_min = -tm->tm_min;
     382                 :           0 :                         tm->tm_sec = -tm->tm_sec;
     383                 :           0 :                         *fsec = -(*fsec);
     384                 :             :                     }
     385                 :             : 
     386                 :             :                     /*
     387                 :             :                      * Set the next type to be a day, if units are not
     388                 :             :                      * specified. This handles the case of '1 +02:03' since we
     389                 :             :                      * are reading right to left.
     390                 :             :                      */
     391                 :           0 :                     type = DTK_DAY;
     392                 :           0 :                     tmask = DTK_M(TZ);
     393                 :           0 :                     break;
     394                 :             :                 }
     395                 :             :                 pg_fallthrough;
     396                 :             : 
     397                 :             :             case DTK_DATE:
     398                 :             :             case DTK_NUMBER:
     399         [ -  + ]:          88 :                 if (type == IGNORE_DTF)
     400                 :             :                 {
     401                 :             :                     /* use typmod to decide what rightmost field is */
     402   [ #  #  #  #  :           0 :                     switch (range)
                #  #  # ]
     403                 :             :                     {
     404                 :           0 :                         case INTERVAL_MASK(YEAR):
     405                 :           0 :                             type = DTK_YEAR;
     406                 :           0 :                             break;
     407                 :           0 :                         case INTERVAL_MASK(MONTH):
     408                 :             :                         case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
     409                 :           0 :                             type = DTK_MONTH;
     410                 :           0 :                             break;
     411                 :           0 :                         case INTERVAL_MASK(DAY):
     412                 :           0 :                             type = DTK_DAY;
     413                 :           0 :                             break;
     414                 :           0 :                         case INTERVAL_MASK(HOUR):
     415                 :             :                         case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
     416                 :             :                         case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
     417                 :             :                         case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
     418                 :           0 :                             type = DTK_HOUR;
     419                 :           0 :                             break;
     420                 :           0 :                         case INTERVAL_MASK(MINUTE):
     421                 :             :                         case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
     422                 :           0 :                             type = DTK_MINUTE;
     423                 :           0 :                             break;
     424                 :           0 :                         case INTERVAL_MASK(SECOND):
     425                 :             :                         case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
     426                 :             :                         case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
     427                 :           0 :                             type = DTK_SECOND;
     428                 :           0 :                             break;
     429                 :           0 :                         default:
     430                 :           0 :                             type = DTK_SECOND;
     431                 :           0 :                             break;
     432                 :             :                     }
     433                 :             :                 }
     434                 :             : 
     435                 :          88 :                 errno = 0;
     436                 :          88 :                 val = strtoint(field[i], &cp, 10);
     437         [ -  + ]:          88 :                 if (errno == ERANGE)
     438                 :           0 :                     return DTERR_FIELD_OVERFLOW;
     439                 :             : 
     440         [ -  + ]:          88 :                 if (*cp == '-')
     441                 :             :                 {
     442                 :             :                     /* SQL "years-months" syntax */
     443                 :             :                     int         val2;
     444                 :             : 
     445                 :           0 :                     val2 = strtoint(cp + 1, &cp, 10);
     446   [ #  #  #  #  :           0 :                     if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
                   #  # ]
     447                 :           0 :                         return DTERR_FIELD_OVERFLOW;
     448         [ #  # ]:           0 :                     if (*cp != '\0')
     449                 :           0 :                         return DTERR_BAD_FORMAT;
     450                 :           0 :                     type = DTK_MONTH;
     451         [ #  # ]:           0 :                     if (*field[i] == '-')
     452                 :           0 :                         val2 = -val2;
     453                 :           0 :                     val = val * MONTHS_PER_YEAR + val2;
     454                 :           0 :                     fval = 0;
     455                 :             :                 }
     456         [ -  + ]:          88 :                 else if (*cp == '.')
     457                 :             :                 {
     458                 :           0 :                     errno = 0;
     459                 :           0 :                     fval = strtod(cp, &cp);
     460   [ #  #  #  # ]:           0 :                     if (*cp != '\0' || errno != 0)
     461                 :           0 :                         return DTERR_BAD_FORMAT;
     462                 :             : 
     463         [ #  # ]:           0 :                     if (*field[i] == '-')
     464                 :           0 :                         fval = -fval;
     465                 :             :                 }
     466         [ +  - ]:          88 :                 else if (*cp == '\0')
     467                 :          88 :                     fval = 0;
     468                 :             :                 else
     469                 :           0 :                     return DTERR_BAD_FORMAT;
     470                 :             : 
     471                 :          88 :                 tmask = 0;      /* DTK_M(type); */
     472                 :             : 
     473   [ -  -  +  +  :          88 :                 switch (type)
          +  +  -  -  +  
             -  -  -  - ]
     474                 :             :                 {
     475                 :           0 :                     case DTK_MICROSEC:
     476                 :           0 :                         *fsec += rint(val + fval);
     477                 :           0 :                         tmask = DTK_M(MICROSECOND);
     478                 :           0 :                         break;
     479                 :             : 
     480                 :           0 :                     case DTK_MILLISEC:
     481                 :           0 :                         *fsec += rint((val + fval) * 1000);
     482                 :           0 :                         tmask = DTK_M(MILLISECOND);
     483                 :           0 :                         break;
     484                 :             : 
     485                 :          10 :                     case DTK_SECOND:
     486                 :          10 :                         tm->tm_sec += val;
     487                 :          10 :                         *fsec += rint(fval * 1000000);
     488                 :             : 
     489                 :             :                         /*
     490                 :             :                          * If any subseconds were specified, consider this
     491                 :             :                          * microsecond and millisecond input as well.
     492                 :             :                          */
     493         [ +  - ]:          10 :                         if (fval == 0)
     494                 :          10 :                             tmask = DTK_M(SECOND);
     495                 :             :                         else
     496                 :           0 :                             tmask = DTK_ALL_SECS_M;
     497                 :          10 :                         break;
     498                 :             : 
     499                 :          14 :                     case DTK_MINUTE:
     500                 :          14 :                         tm->tm_min += val;
     501                 :          14 :                         AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE);
     502                 :          14 :                         tmask = DTK_M(MINUTE);
     503                 :          14 :                         break;
     504                 :             : 
     505                 :          50 :                     case DTK_HOUR:
     506                 :          50 :                         tm->tm_hour += val;
     507                 :          50 :                         AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR);
     508                 :          50 :                         tmask = DTK_M(HOUR);
     509                 :          50 :                         type = DTK_DAY;
     510                 :          50 :                         break;
     511                 :             : 
     512                 :          12 :                     case DTK_DAY:
     513                 :          12 :                         tm->tm_mday += val;
     514                 :          12 :                         AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY);
     515         [ -  + ]:          12 :                         tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
     516                 :          12 :                         break;
     517                 :             : 
     518                 :           0 :                     case DTK_WEEK:
     519                 :           0 :                         tm->tm_mday += val * 7;
     520                 :           0 :                         AdjustFractDays(fval, tm, fsec, 7);
     521         [ #  # ]:           0 :                         tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
     522                 :           0 :                         break;
     523                 :             : 
     524                 :           0 :                     case DTK_MONTH:
     525                 :           0 :                         tm->tm_mon += val;
     526                 :           0 :                         AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH);
     527                 :           0 :                         tmask = DTK_M(MONTH);
     528                 :           0 :                         break;
     529                 :             : 
     530                 :           2 :                     case DTK_YEAR:
     531                 :           2 :                         tm->tm_year += val;
     532                 :           2 :                         tm->tm_mon += rint(fval * MONTHS_PER_YEAR);
     533         [ -  + ]:           2 :                         tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
     534                 :           2 :                         break;
     535                 :             : 
     536                 :           0 :                     case DTK_DECADE:
     537                 :           0 :                         tm->tm_year += val * 10;
     538                 :           0 :                         tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 10);
     539         [ #  # ]:           0 :                         tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
     540                 :           0 :                         break;
     541                 :             : 
     542                 :           0 :                     case DTK_CENTURY:
     543                 :           0 :                         tm->tm_year += val * 100;
     544                 :           0 :                         tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 100);
     545         [ #  # ]:           0 :                         tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
     546                 :           0 :                         break;
     547                 :             : 
     548                 :           0 :                     case DTK_MILLENNIUM:
     549                 :           0 :                         tm->tm_year += val * 1000;
     550                 :           0 :                         tm->tm_mon += rint(fval * MONTHS_PER_YEAR * 1000);
     551         [ #  # ]:           0 :                         tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR);
     552                 :           0 :                         break;
     553                 :             : 
     554                 :           0 :                     default:
     555                 :           0 :                         return DTERR_BAD_FORMAT;
     556                 :             :                 }
     557                 :          88 :                 break;
     558                 :             : 
     559                 :          88 :             case DTK_STRING:
     560                 :             :             case DTK_SPECIAL:
     561                 :          88 :                 type = DecodeUnits(i, field[i], &val);
     562         [ -  + ]:          88 :                 if (type == IGNORE_DTF)
     563                 :           0 :                     continue;
     564                 :             : 
     565                 :          88 :                 tmask = 0;      /* DTK_M(type); */
     566   [ +  -  -  + ]:          88 :                 switch (type)
     567                 :             :                 {
     568                 :          86 :                     case UNITS:
     569                 :          86 :                         type = val;
     570                 :          86 :                         break;
     571                 :             : 
     572                 :           0 :                     case AGO:
     573                 :           0 :                         is_before = true;
     574                 :           0 :                         type = val;
     575                 :           0 :                         break;
     576                 :             : 
     577                 :           0 :                     case RESERV:
     578                 :           0 :                         tmask = (DTK_DATE_M | DTK_TIME_M);
     579                 :           0 :                         *dtype = val;
     580                 :           0 :                         break;
     581                 :             : 
     582                 :           2 :                     default:
     583                 :           2 :                         return DTERR_BAD_FORMAT;
     584                 :             :                 }
     585                 :          86 :                 break;
     586                 :             : 
     587                 :           0 :             default:
     588                 :           0 :                 return DTERR_BAD_FORMAT;
     589                 :             :         }
     590                 :             : 
     591         [ -  + ]:         176 :         if (tmask & fmask)
     592                 :           0 :             return DTERR_BAD_FORMAT;
     593                 :         176 :         fmask |= tmask;
     594                 :             :     }
     595                 :             : 
     596                 :             :     /* ensure that at least one time field has been found */
     597         [ -  + ]:          56 :     if (fmask == 0)
     598                 :           0 :         return DTERR_BAD_FORMAT;
     599                 :             : 
     600                 :             :     /* ensure fractional seconds are fractional */
     601         [ -  + ]:          56 :     if (*fsec != 0)
     602                 :             :     {
     603                 :             :         int         sec;
     604                 :             : 
     605                 :           0 :         sec = *fsec / USECS_PER_SEC;
     606                 :           0 :         *fsec -= sec * USECS_PER_SEC;
     607                 :           0 :         tm->tm_sec += sec;
     608                 :             :     }
     609                 :             : 
     610                 :             :     /*----------
     611                 :             :      * The SQL standard defines the interval literal
     612                 :             :      *   '-1 1:00:00'
     613                 :             :      * to mean "negative 1 days and negative 1 hours", while Postgres
     614                 :             :      * traditionally treats this as meaning "negative 1 days and positive
     615                 :             :      * 1 hours".  In SQL_STANDARD intervalstyle, we apply the leading sign
     616                 :             :      * to all fields if there are no other explicit signs.
     617                 :             :      *
     618                 :             :      * We leave the signs alone if there are additional explicit signs.
     619                 :             :      * This protects us against misinterpreting postgres-style dump output,
     620                 :             :      * since the postgres-style output code has always put an explicit sign on
     621                 :             :      * all fields following a negative field.  But note that SQL-spec output
     622                 :             :      * is ambiguous and can be misinterpreted on load!  (So it's best practice
     623                 :             :      * to dump in postgres style, not SQL style.)
     624                 :             :      *----------
     625                 :             :      */
     626   [ -  +  -  - ]:          56 :     if (IntervalStyle == INTSTYLE_SQL_STANDARD && *field[0] == '-')
     627                 :             :     {
     628                 :             :         /* Check for additional explicit signs */
     629                 :           0 :         bool        more_signs = false;
     630                 :             : 
     631         [ #  # ]:           0 :         for (i = 1; i < nf; i++)
     632                 :             :         {
     633   [ #  #  #  # ]:           0 :             if (*field[i] == '-' || *field[i] == '+')
     634                 :             :             {
     635                 :           0 :                 more_signs = true;
     636                 :           0 :                 break;
     637                 :             :             }
     638                 :             :         }
     639                 :             : 
     640         [ #  # ]:           0 :         if (!more_signs)
     641                 :             :         {
     642                 :             :             /*
     643                 :             :              * Rather than re-determining which field was field[0], just force
     644                 :             :              * 'em all negative.
     645                 :             :              */
     646         [ #  # ]:           0 :             if (*fsec > 0)
     647                 :           0 :                 *fsec = -(*fsec);
     648         [ #  # ]:           0 :             if (tm->tm_sec > 0)
     649                 :           0 :                 tm->tm_sec = -tm->tm_sec;
     650         [ #  # ]:           0 :             if (tm->tm_min > 0)
     651                 :           0 :                 tm->tm_min = -tm->tm_min;
     652         [ #  # ]:           0 :             if (tm->tm_hour > 0)
     653                 :           0 :                 tm->tm_hour = -tm->tm_hour;
     654         [ #  # ]:           0 :             if (tm->tm_mday > 0)
     655                 :           0 :                 tm->tm_mday = -tm->tm_mday;
     656         [ #  # ]:           0 :             if (tm->tm_mon > 0)
     657                 :           0 :                 tm->tm_mon = -tm->tm_mon;
     658         [ #  # ]:           0 :             if (tm->tm_year > 0)
     659                 :           0 :                 tm->tm_year = -tm->tm_year;
     660                 :             :         }
     661                 :             :     }
     662                 :             : 
     663                 :             :     /* finally, AGO negates everything */
     664         [ -  + ]:          56 :     if (is_before)
     665                 :             :     {
     666                 :           0 :         *fsec = -(*fsec);
     667                 :           0 :         tm->tm_sec = -tm->tm_sec;
     668                 :           0 :         tm->tm_min = -tm->tm_min;
     669                 :           0 :         tm->tm_hour = -tm->tm_hour;
     670                 :           0 :         tm->tm_mday = -tm->tm_mday;
     671                 :           0 :         tm->tm_mon = -tm->tm_mon;
     672                 :           0 :         tm->tm_year = -tm->tm_year;
     673                 :             :     }
     674                 :             : 
     675                 :          56 :     return 0;
     676                 :             : }
     677                 :             : 
     678                 :             : 
     679                 :             : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
     680                 :             : static char *
     681                 :         540 : AddVerboseIntPart(char *cp, int value, const char *units,
     682                 :             :                   bool *is_zero, bool *is_before)
     683                 :             : {
     684         [ +  + ]:         540 :     if (value == 0)
     685                 :         390 :         return cp;
     686                 :             :     /* first nonzero value sets is_before */
     687         [ +  + ]:         150 :     if (*is_zero)
     688                 :             :     {
     689                 :         108 :         *is_before = (value < 0);
     690                 :         108 :         value = abs(value);
     691                 :             :     }
     692         [ -  + ]:          42 :     else if (*is_before)
     693                 :           0 :         value = -value;
     694         [ +  + ]:         150 :     sprintf(cp, " %d %s%s", value, units, (value == 1) ? "" : "s");
     695                 :         150 :     *is_zero = false;
     696                 :         150 :     return cp + strlen(cp);
     697                 :             : }
     698                 :             : 
     699                 :             : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
     700                 :             : static char *
     701                 :           0 : AddPostgresIntPart(char *cp, int value, const char *units,
     702                 :             :                    bool *is_zero, bool *is_before)
     703                 :             : {
     704         [ #  # ]:           0 :     if (value == 0)
     705                 :           0 :         return cp;
     706         [ #  # ]:           0 :     sprintf(cp, "%s%s%d %s%s",
     707         [ #  # ]:           0 :             (!*is_zero) ? " " : "",
     708   [ #  #  #  # ]:           0 :             (*is_before && value > 0) ? "+" : "",
     709                 :             :             value,
     710                 :             :             units,
     711                 :             :             (value != 1) ? "s" : "");
     712                 :             : 
     713                 :             :     /*
     714                 :             :      * Each nonzero field sets is_before for (only) the next one.  This is a
     715                 :             :      * tad bizarre but it's how it worked before...
     716                 :             :      */
     717                 :           0 :     *is_before = (value < 0);
     718                 :           0 :     *is_zero = false;
     719                 :           0 :     return cp + strlen(cp);
     720                 :             : }
     721                 :             : 
     722                 :             : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
     723                 :             : static char *
     724                 :           0 : AddISO8601IntPart(char *cp, int value, char units)
     725                 :             : {
     726         [ #  # ]:           0 :     if (value == 0)
     727                 :           0 :         return cp;
     728                 :           0 :     sprintf(cp, "%d%c", value, units);
     729                 :           0 :     return cp + strlen(cp);
     730                 :             : }
     731                 :             : 
     732                 :             : /* copy&pasted from .../src/backend/utils/adt/datetime.c */
     733                 :             : static void
     734                 :          20 : AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
     735                 :             : {
     736         [ +  - ]:          20 :     if (fsec == 0)
     737                 :             :     {
     738         [ -  + ]:          20 :         if (fillzeros)
     739                 :           0 :             sprintf(cp, "%02d", abs(sec));
     740                 :             :         else
     741                 :          20 :             sprintf(cp, "%d", abs(sec));
     742                 :             :     }
     743                 :             :     else
     744                 :             :     {
     745         [ #  # ]:           0 :         if (fillzeros)
     746                 :           0 :             sprintf(cp, "%02d.%0*d", abs(sec), precision, abs(fsec));
     747                 :             :         else
     748                 :           0 :             sprintf(cp, "%d.%0*d", abs(sec), precision, abs(fsec));
     749                 :           0 :         TrimTrailingZeros(cp);
     750                 :             :     }
     751                 :          20 : }
     752                 :             : 
     753                 :             : 
     754                 :             : /*
     755                 :             :  * copy&pasted from .../src/backend/utils/adt/datetime.c
     756                 :             :  *
     757                 :             :  * Change pg_tm to tm
     758                 :             :  */
     759                 :             : 
     760                 :             : void
     761                 :         108 : EncodeInterval(struct /* pg_ */ tm *tm, fsec_t fsec, int style, char *str)
     762                 :             : {
     763                 :         108 :     char       *cp = str;
     764                 :         108 :     int         year = tm->tm_year;
     765                 :         108 :     int         mon = tm->tm_mon;
     766                 :         108 :     int         mday = tm->tm_mday;
     767                 :         108 :     int         hour = tm->tm_hour;
     768                 :         108 :     int         min = tm->tm_min;
     769                 :         108 :     int         sec = tm->tm_sec;
     770                 :         108 :     bool        is_before = false;
     771                 :         108 :     bool        is_zero = true;
     772                 :             : 
     773                 :             :     /*
     774                 :             :      * The sign of year and month are guaranteed to match, since they are
     775                 :             :      * stored internally as "month". But we'll need to check for is_before and
     776                 :             :      * is_zero when determining the signs of day and hour/minute/seconds
     777                 :             :      * fields.
     778                 :             :      */
     779   [ -  -  -  + ]:         108 :     switch (style)
     780                 :             :     {
     781                 :             :             /* SQL Standard interval format */
     782                 :           0 :         case INTSTYLE_SQL_STANDARD:
     783                 :             :             {
     784   [ #  #  #  # ]:           0 :                 bool        has_negative = year < 0 || mon < 0 ||
     785   [ #  #  #  # ]:           0 :                     mday < 0 || hour < 0 ||
     786   [ #  #  #  #  :           0 :                     min < 0 || sec < 0 || fsec < 0;
                   #  # ]
     787   [ #  #  #  # ]:           0 :                 bool        has_positive = year > 0 || mon > 0 ||
     788   [ #  #  #  # ]:           0 :                     mday > 0 || hour > 0 ||
     789   [ #  #  #  #  :           0 :                     min > 0 || sec > 0 || fsec > 0;
                   #  # ]
     790   [ #  #  #  # ]:           0 :                 bool        has_year_month = year != 0 || mon != 0;
     791   [ #  #  #  # ]:           0 :                 bool        has_day_time = mday != 0 || hour != 0 ||
     792   [ #  #  #  #  :           0 :                     min != 0 || sec != 0 || fsec != 0;
                   #  # ]
     793                 :           0 :                 bool        has_day = mday != 0;
     794   [ #  #  #  # ]:           0 :                 bool        sql_standard_value = !(has_negative && has_positive) &&
     795   [ #  #  #  # ]:           0 :                     !(has_year_month && has_day_time);
     796                 :             : 
     797                 :             :                 /*
     798                 :             :                  * SQL Standard wants only 1 "<sign>" preceding the whole
     799                 :             :                  * interval ... but can't do that if mixed signs.
     800                 :             :                  */
     801   [ #  #  #  # ]:           0 :                 if (has_negative && sql_standard_value)
     802                 :             :                 {
     803                 :           0 :                     *cp++ = '-';
     804                 :           0 :                     year = -year;
     805                 :           0 :                     mon = -mon;
     806                 :           0 :                     mday = -mday;
     807                 :           0 :                     hour = -hour;
     808                 :           0 :                     min = -min;
     809                 :           0 :                     sec = -sec;
     810                 :           0 :                     fsec = -fsec;
     811                 :             :                 }
     812                 :             : 
     813   [ #  #  #  # ]:           0 :                 if (!has_negative && !has_positive)
     814                 :             :                 {
     815                 :           0 :                     sprintf(cp, "0");
     816                 :             :                 }
     817         [ #  # ]:           0 :                 else if (!sql_standard_value)
     818                 :             :                 {
     819                 :             :                     /*
     820                 :             :                      * For non sql-standard interval values, force outputting
     821                 :             :                      * the signs to avoid ambiguities with intervals with
     822                 :             :                      * mixed sign components.
     823                 :             :                      */
     824   [ #  #  #  # ]:           0 :                     char        year_sign = (year < 0 || mon < 0) ? '-' : '+';
     825         [ #  # ]:           0 :                     char        day_sign = (mday < 0) ? '-' : '+';
     826   [ #  #  #  #  :           0 :                     char        sec_sign = (hour < 0 || min < 0 ||
                   #  # ]
     827         [ #  # ]:           0 :                                             sec < 0 || fsec < 0) ? '-' : '+';
     828                 :             : 
     829                 :           0 :                     sprintf(cp, "%c%d-%d %c%d %c%d:%02d:",
     830                 :             :                             year_sign, abs(year), abs(mon),
     831                 :             :                             day_sign, abs(mday),
     832                 :             :                             sec_sign, abs(hour), abs(min));
     833                 :           0 :                     cp += strlen(cp);
     834                 :           0 :                     AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
     835                 :             :                 }
     836         [ #  # ]:           0 :                 else if (has_year_month)
     837                 :             :                 {
     838                 :           0 :                     sprintf(cp, "%d-%d", year, mon);
     839                 :             :                 }
     840         [ #  # ]:           0 :                 else if (has_day)
     841                 :             :                 {
     842                 :           0 :                     sprintf(cp, "%d %d:%02d:", mday, hour, min);
     843                 :           0 :                     cp += strlen(cp);
     844                 :           0 :                     AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
     845                 :             :                 }
     846                 :             :                 else
     847                 :             :                 {
     848                 :           0 :                     sprintf(cp, "%d:%02d:", hour, min);
     849                 :           0 :                     cp += strlen(cp);
     850                 :           0 :                     AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
     851                 :             :                 }
     852                 :             :             }
     853                 :           0 :             break;
     854                 :             : 
     855                 :             :             /* ISO 8601 "time-intervals by duration only" */
     856                 :           0 :         case INTSTYLE_ISO_8601:
     857                 :             :             /* special-case zero to avoid printing nothing */
     858   [ #  #  #  #  :           0 :             if (year == 0 && mon == 0 && mday == 0 &&
             #  #  #  # ]
     859   [ #  #  #  #  :           0 :                 hour == 0 && min == 0 && sec == 0 && fsec == 0)
                   #  # ]
     860                 :             :             {
     861                 :           0 :                 sprintf(cp, "PT0S");
     862                 :           0 :                 break;
     863                 :             :             }
     864                 :           0 :             *cp++ = 'P';
     865                 :           0 :             cp = AddISO8601IntPart(cp, year, 'Y');
     866                 :           0 :             cp = AddISO8601IntPart(cp, mon, 'M');
     867                 :           0 :             cp = AddISO8601IntPart(cp, mday, 'D');
     868   [ #  #  #  #  :           0 :             if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
             #  #  #  # ]
     869                 :           0 :                 *cp++ = 'T';
     870                 :           0 :             cp = AddISO8601IntPart(cp, hour, 'H');
     871                 :           0 :             cp = AddISO8601IntPart(cp, min, 'M');
     872   [ #  #  #  # ]:           0 :             if (sec != 0 || fsec != 0)
     873                 :             :             {
     874   [ #  #  #  # ]:           0 :                 if (sec < 0 || fsec < 0)
     875                 :           0 :                     *cp++ = '-';
     876                 :           0 :                 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
     877                 :           0 :                 cp += strlen(cp);
     878                 :           0 :                 *cp++ = 'S';
     879                 :           0 :                 *cp = '\0';
     880                 :             :             }
     881                 :           0 :             break;
     882                 :             : 
     883                 :             :             /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
     884                 :           0 :         case INTSTYLE_POSTGRES:
     885                 :           0 :             cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
     886                 :           0 :             cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
     887                 :           0 :             cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
     888   [ #  #  #  #  :           0 :             if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
          #  #  #  #  #  
                      # ]
     889                 :             :             {
     890   [ #  #  #  #  :           0 :                 bool        minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
             #  #  #  # ]
     891                 :             : 
     892         [ #  # ]:           0 :                 sprintf(cp, "%s%s%02d:%02d:",
     893         [ #  # ]:           0 :                         is_zero ? "" : " ",
     894         [ #  # ]:           0 :                         (minus ? "-" : (is_before ? "+" : "")),
     895                 :             :                         abs(hour), abs(min));
     896                 :           0 :                 cp += strlen(cp);
     897                 :           0 :                 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
     898                 :             :             }
     899                 :           0 :             break;
     900                 :             : 
     901                 :             :             /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
     902                 :         108 :         case INTSTYLE_POSTGRES_VERBOSE:
     903                 :             :         default:
     904                 :         108 :             strcpy(cp, "@");
     905                 :         108 :             cp++;
     906                 :         108 :             cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
     907                 :         108 :             cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
     908                 :         108 :             cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
     909                 :         108 :             cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
     910                 :         108 :             cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
     911   [ +  +  -  + ]:         108 :             if (sec != 0 || fsec != 0)
     912                 :             :             {
     913                 :          20 :                 *cp++ = ' ';
     914   [ +  -  -  +  :          20 :                 if (sec < 0 || (sec == 0 && fsec < 0))
                   -  - ]
     915                 :             :                 {
     916         [ #  # ]:           0 :                     if (is_zero)
     917                 :           0 :                         is_before = true;
     918         [ #  # ]:           0 :                     else if (!is_before)
     919                 :           0 :                         *cp++ = '-';
     920                 :             :                 }
     921         [ -  + ]:          20 :                 else if (is_before)
     922                 :           0 :                     *cp++ = '-';
     923                 :          20 :                 AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
     924                 :          20 :                 cp += strlen(cp);
     925                 :             :                 /* We output "ago", not negatives, so use abs(). */
     926                 :          20 :                 sprintf(cp, " sec%s",
     927   [ +  +  -  + ]:          20 :                         (abs(sec) != 1 || fsec != 0) ? "s" : "");
     928                 :          20 :                 is_zero = false;
     929                 :             :             }
     930                 :             :             /* identically zero? then put in a unitless zero... */
     931         [ -  + ]:         108 :             if (is_zero)
     932                 :           0 :                 strcat(cp, " 0");
     933         [ -  + ]:         108 :             if (is_before)
     934                 :           0 :                 strcat(cp, " ago");
     935                 :         108 :             break;
     936                 :             :     }
     937                 :         108 : }
     938                 :             : 
     939                 :             : 
     940                 :             : /*
     941                 :             :  * interval2tm()
     942                 :             :  * Convert an interval data type to a tm structure.
     943                 :             :  */
     944                 :             : static int
     945                 :         108 : interval2tm(interval span, struct tm *tm, fsec_t *fsec)
     946                 :             : {
     947                 :             :     int64       time;
     948                 :             : 
     949         [ +  + ]:         108 :     if (span.month != 0)
     950                 :             :     {
     951                 :           6 :         tm->tm_year = span.month / MONTHS_PER_YEAR;
     952                 :           6 :         tm->tm_mon = span.month % MONTHS_PER_YEAR;
     953                 :             :     }
     954                 :             :     else
     955                 :             :     {
     956                 :         102 :         tm->tm_year = 0;
     957                 :         102 :         tm->tm_mon = 0;
     958                 :             :     }
     959                 :             : 
     960                 :         108 :     time = span.time;
     961                 :             : 
     962                 :         108 :     tm->tm_mday = time / USECS_PER_DAY;
     963                 :         108 :     time -= tm->tm_mday * USECS_PER_DAY;
     964                 :         108 :     tm->tm_hour = time / USECS_PER_HOUR;
     965                 :         108 :     time -= tm->tm_hour * USECS_PER_HOUR;
     966                 :         108 :     tm->tm_min = time / USECS_PER_MINUTE;
     967                 :         108 :     time -= tm->tm_min * USECS_PER_MINUTE;
     968                 :         108 :     tm->tm_sec = time / USECS_PER_SEC;
     969                 :         108 :     *fsec = time - (tm->tm_sec * USECS_PER_SEC);
     970                 :             : 
     971                 :         108 :     return 0;
     972                 :             : }                               /* interval2tm() */
     973                 :             : 
     974                 :             : static int
     975                 :          56 : tm2interval(struct tm *tm, fsec_t fsec, interval * span)
     976                 :             : {
     977         [ +  - ]:          56 :     if ((double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon > INT_MAX ||
     978         [ -  + ]:          56 :         (double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon < INT_MIN)
     979                 :           0 :         return -1;
     980                 :          56 :     span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
     981                 :          56 :     span->time = (((((((tm->tm_mday * INT64CONST(24)) +
     982                 :          56 :                        tm->tm_hour) * INT64CONST(60)) +
     983                 :          56 :                      tm->tm_min) * INT64CONST(60)) +
     984                 :          56 :                    tm->tm_sec) * USECS_PER_SEC) + fsec;
     985                 :             : 
     986                 :          56 :     return 0;
     987                 :             : }                               /* tm2interval() */
     988                 :             : 
     989                 :             : interval *
     990                 :          32 : PGTYPESinterval_new(void)
     991                 :             : {
     992                 :             :     interval   *result;
     993                 :             : 
     994                 :          32 :     result = (interval *) pgtypes_alloc(sizeof(interval));
     995                 :             :     /* result can be NULL if we run out of memory */
     996                 :          32 :     return result;
     997                 :             : }
     998                 :             : 
     999                 :             : void
    1000                 :          26 : PGTYPESinterval_free(interval * intvl)
    1001                 :             : {
    1002                 :          26 :     free(intvl);
    1003                 :          26 : }
    1004                 :             : 
    1005                 :             : interval *
    1006                 :          58 : PGTYPESinterval_from_asc(char *str, char **endptr)
    1007                 :             : {
    1008                 :          58 :     interval   *result = NULL;
    1009                 :             :     fsec_t      fsec;
    1010                 :             :     struct tm   tt,
    1011                 :          58 :                *tm = &tt;
    1012                 :             :     int         dtype;
    1013                 :             :     int         nf;
    1014                 :             :     char       *field[MAXDATEFIELDS];
    1015                 :             :     int         ftype[MAXDATEFIELDS];
    1016                 :             :     char        lowstr[MAXDATELEN + MAXDATEFIELDS];
    1017                 :             :     char       *realptr;
    1018         [ +  + ]:          58 :     char      **ptr = (endptr != NULL) ? endptr : &realptr;
    1019                 :             : 
    1020                 :          58 :     tm->tm_year = 0;
    1021                 :          58 :     tm->tm_mon = 0;
    1022                 :          58 :     tm->tm_mday = 0;
    1023                 :          58 :     tm->tm_hour = 0;
    1024                 :          58 :     tm->tm_min = 0;
    1025                 :          58 :     tm->tm_sec = 0;
    1026                 :          58 :     fsec = 0;
    1027                 :             : 
    1028         [ -  + ]:          58 :     if (strlen(str) > MAXDATELEN)
    1029                 :             :     {
    1030                 :           0 :         errno = PGTYPES_INTVL_BAD_INTERVAL;
    1031                 :           0 :         return NULL;
    1032                 :             :     }
    1033                 :             : 
    1034   [ +  -  +  + ]:         116 :     if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
    1035         [ +  - ]:          60 :         (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0 &&
    1036                 :           2 :          DecodeISO8601Interval(str, &dtype, tm, &fsec) != 0))
    1037                 :             :     {
    1038                 :           2 :         errno = PGTYPES_INTVL_BAD_INTERVAL;
    1039                 :           2 :         return NULL;
    1040                 :             :     }
    1041                 :             : 
    1042                 :          56 :     result = (interval *) pgtypes_alloc(sizeof(interval));
    1043         [ -  + ]:          56 :     if (!result)
    1044                 :           0 :         return NULL;
    1045                 :             : 
    1046         [ -  + ]:          56 :     if (dtype != DTK_DELTA)
    1047                 :             :     {
    1048                 :           0 :         errno = PGTYPES_INTVL_BAD_INTERVAL;
    1049                 :           0 :         free(result);
    1050                 :           0 :         return NULL;
    1051                 :             :     }
    1052                 :             : 
    1053         [ -  + ]:          56 :     if (tm2interval(tm, fsec, result) != 0)
    1054                 :             :     {
    1055                 :           0 :         errno = PGTYPES_INTVL_BAD_INTERVAL;
    1056                 :           0 :         free(result);
    1057                 :           0 :         return NULL;
    1058                 :             :     }
    1059                 :             : 
    1060                 :          56 :     errno = 0;
    1061                 :          56 :     return result;
    1062                 :             : }
    1063                 :             : 
    1064                 :             : char *
    1065                 :         108 : PGTYPESinterval_to_asc(interval * span)
    1066                 :             : {
    1067                 :             :     struct tm   tt,
    1068                 :         108 :                *tm = &tt;
    1069                 :             :     fsec_t      fsec;
    1070                 :             :     char        buf[MAXDATELEN + 1];
    1071                 :         108 :     int         IntervalStyle = INTSTYLE_POSTGRES_VERBOSE;
    1072                 :             : 
    1073         [ -  + ]:         108 :     if (interval2tm(*span, tm, &fsec) != 0)
    1074                 :             :     {
    1075                 :           0 :         errno = PGTYPES_INTVL_BAD_INTERVAL;
    1076                 :           0 :         return NULL;
    1077                 :             :     }
    1078                 :             : 
    1079                 :         108 :     EncodeInterval(tm, fsec, IntervalStyle, buf);
    1080                 :             : 
    1081                 :         108 :     return pgtypes_strdup(buf);
    1082                 :             : }
    1083                 :             : 
    1084                 :             : int
    1085                 :          34 : PGTYPESinterval_copy(interval * intvlsrc, interval * intvldest)
    1086                 :             : {
    1087                 :          34 :     intvldest->time = intvlsrc->time;
    1088                 :          34 :     intvldest->month = intvlsrc->month;
    1089                 :             : 
    1090                 :          34 :     return 0;
    1091                 :             : }
        

Generated by: LCOV version 2.0-1