LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - interval.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 33.3 % 604 201
Test Date: 2026-05-30 00:16:21 Functions: 73.7 % 19 14
Legend: Lines:     hit not hit

            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