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

Generated by: LCOV version 1.14