Patch for SQL-standard negative valued year-month literals - Mailing list pgsql-hackers

From Ron Mayer
Subject Patch for SQL-standard negative valued year-month literals
Date
Msg-id 48D0170D.6000100@cheapcomplexdevices.com
Whole thread Raw
In response to Proposed patch: make SQL interval-literal syntax work per spec  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: Patch for SQL-standard negative valued year-month literals  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-hackers
Tom Lane wrote:
> ...  SQL-spec interval literals.  I decided to go look at exactly
> how unfinished it was, and it turns out that it's actually pretty close.
> Hence the attached proposed patch ;-)

Short summary:
 I think this patch fixes a bug with sql-spec negative interval literals.

Longer.

I believe this short (4 lines of code & 1 line of comment) patch (below)
fixes the way we handle SQL-standard negative-valued year-month interval
strings.

In particular, if I read the spec right (relevant excerpts below), the
string '-1-1' is a vaild SQL-200N "year-month" interval meaning a
negative year and a month - because the spec only allows a <sign>
in the beginning of the unquoted interval string:   <unquoted interval string> ::=     [ <sign> ] { <year-month
literal>| <day-time literal> }
 
Current HEAD interprets '-1-1' as "-1 days -1 hours".  8.3 doesn't
recognize it at all.

Assuming I read the spec right, are there any problems with this,
and if not, could I ask that the patch at the end of this email
be applied?
   Ron



===============================================================================
== with this patch
===============================================================================

regression=# select interval '-1-1';     interval
------------------ -1 years -1 mons
(1 row)

===============================================================================
== without this patch
===============================================================================

regression=# select interval '-1-1';     interval
------------------- -1 days -01:00:00
(1 row)

===============================================================================
== 8.3
===============================================================================

regression=# select interval '-1-1';
ERROR:  invalid input syntax for type interval: "-1-1"

===============================================================================
== I think the relevant part of SQL 200N
===============================================================================

<interval string> ::=  <quote> <unquoted interval string> <quote>

<unquoted interval string> ::=  [ <sign> ] { <year-month literal> | <day-time literal> }

<year-month literal> ::=    <years value> [ <minus sign> <months value> ]  | <months value>

<years value> ::=  <datetime value>
<months value> ::=  <datetime value>

<datetime value> ::=  <unsigned integer>

... If SV is a negative interval, then <sign> shall be specified
within <unquoted interval string> in the literal Y.

===============================================================================
== The patch
===============================================================================

*** a/src/backend/utils/adt/datetime.c
--- b/src/backend/utils/adt/datetime.c
***************
*** 609,621 **** ParseDateTime(const char *timestr, char *workbuf, size_t buflen,              /* soak up leading
whitespace*/              while (isspace((unsigned char) *cp))                  cp++;
 
!             /* numeric timezone? */              if (isdigit((unsigned char) *cp))              {
ftype[nf]= DTK_TZ;                  APPEND_CHAR(bufp, bufend, *cp++);                  while (isdigit((unsigned char)
*cp)||
 
!                        *cp == ':' || *cp == '.')                      APPEND_CHAR(bufp, bufend, *cp++);
}             /* special? */
 
--- 609,621 ----              /* soak up leading whitespace */              while (isspace((unsigned char) *cp))
         cp++;
 
!             /* numeric timezone?  or sql year-month interval?*/              if (isdigit((unsigned char) *cp))
     {                  ftype[nf] = DTK_TZ;                  APPEND_CHAR(bufp, bufend, *cp++);                  while
(isdigit((unsignedchar) *cp) ||
 
!                        *cp == ':' || *cp == '.' || *cp == '-')                      APPEND_CHAR(bufp, bufend, *cp++);
            }              /* special? */
 
***************
*** 2876,2889 **** DecodeInterval(char **field, int *ftype, int nf, int range,                  {
/*SQL "years-months" syntax */                      int        val2;
 
!                      val2 = strtoi(cp + 1, &cp, 10);                      if (errno == ERANGE || val2 < 0 || val2 >=
MONTHS_PER_YEAR)                         return DTERR_FIELD_OVERFLOW;                      if (*cp != '\0')
            return DTERR_BAD_FORMAT;                      type = DTK_MONTH;
 
!                     val = val * MONTHS_PER_YEAR + val2;                      fval = 0;                  }
    else if (*cp == '.')
 
--- 2876,2890 ----                  {                      /* SQL "years-months" syntax */                      int
  val2;
 
!                     int     sign;
!                     sign = val < 0 ? -1 : 1;                      val2 = strtoi(cp + 1, &cp, 10);
if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)                          return DTERR_FIELD_OVERFLOW;
              if (*cp != '\0')                          return DTERR_BAD_FORMAT;                      type =
DTK_MONTH;
!                     val = val * MONTHS_PER_YEAR + val2*sign;                      fval = 0;                  }
         else if (*cp == '.')
 
================================================================================



pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: EXEC_BACKEND
Next
From: Zdenek Kotala
Date:
Subject: Re: New FSM patch