Non-decimal integer literals
authorPeter Eisentraut <peter@eisentraut.org>
Wed, 14 Dec 2022 04:40:38 +0000 (05:40 +0100)
committerPeter Eisentraut <peter@eisentraut.org>
Wed, 14 Dec 2022 05:17:07 +0000 (06:17 +0100)
Add support for hexadecimal, octal, and binary integer literals:

    0x42F
    0o273
    0b100101

per SQL:202x draft.

This adds support in the lexer as well as in the integer type input
functions.

Reviewed-by: John Naylor <john.naylor@enterprisedb.com>
Reviewed-by: Zhihong Yu <zyu@yugabyte.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/b239564c-cad0-b23e-c57e-166d883cb97d@enterprisedb.com

16 files changed:
doc/src/sgml/syntax.sgml
src/backend/catalog/information_schema.sql
src/backend/catalog/sql_features.txt
src/backend/parser/parse_node.c
src/backend/parser/scan.l
src/backend/utils/adt/numutils.c
src/fe_utils/psqlscan.l
src/interfaces/ecpg/preproc/pgc.l
src/test/regress/expected/int2.out
src/test/regress/expected/int4.out
src/test/regress/expected/int8.out
src/test/regress/expected/numerology.out
src/test/regress/sql/int2.sql
src/test/regress/sql/int4.sql
src/test/regress/sql/int8.sql
src/test/regress/sql/numerology.sql

index 93ad71737f51383291b37bc1bf79e1ac59f81f0e..956182e7c6a8d4dea8525e2159c954b8ae3e939f 100644 (file)
@@ -694,6 +694,40 @@ $function$
 </literallayout>
     </para>
 
+    <para>
+     Additionally, non-decimal integer constants can be used in these forms:
+<synopsis>
+0x<replaceable>hexdigits</replaceable>
+0o<replaceable>octdigits</replaceable>
+0b<replaceable>bindigits</replaceable>
+</synopsis>
+     <replaceable>hexdigits</replaceable> is one or more hexadecimal digits
+     (0-9, A-F), <replaceable>octdigits</replaceable> is one or more octal
+     digits (0-7), <replaceable>bindigits</replaceable> is one or more binary
+     digits (0 or 1).  Hexadecimal digits and the radix prefixes can be in
+     upper or lower case.  Note that only integers can have non-decimal forms,
+     not numbers with fractional parts.
+    </para>
+
+    <para>
+     These are some examples of this:
+<literallayout>0b100101
+0B10011001
+0o273
+0O755
+0x42f
+0XFFFF
+</literallayout>
+    </para>
+
+    <note>
+     <para>
+      Nondecimal integer constants are currently only supported in the range
+      of the <type>bigint</type> type (see <xref
+      linkend="datatype-numeric-table"/>).
+     </para>
+    </note>
+
     <para>
      <indexterm><primary>integer</primary></indexterm>
      <indexterm><primary>bigint</primary></indexterm>
index 18725a02d1fb6ffda3d218033b972a0ff23aac3b..95c27a625e7ea6a0135557dc1cdc77c49eeafe3c 100644 (file)
@@ -119,7 +119,7 @@ RETURN
          WHEN 1700 /*numeric*/ THEN
               CASE WHEN $2 = -1
                    THEN null
-                   ELSE (($2 - 4) >> 16) & 65535
+                   ELSE (($2 - 4) >> 16) & 0xFFFF
                    END
          WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/
          WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
@@ -147,7 +147,7 @@ RETURN
        WHEN $1 IN (1700) THEN
             CASE WHEN $2 = -1
                  THEN null
-                 ELSE ($2 - 4) & 65535
+                 ELSE ($2 - 4) & 0xFFFF
                  END
        ELSE null
   END;
@@ -163,7 +163,7 @@ RETURN
        WHEN $1 IN (1083, 1114, 1184, 1266) /* time, timestamp, same + tz */
            THEN CASE WHEN $2 < 0 THEN 6 ELSE $2 END
        WHEN $1 IN (1186) /* interval */
-           THEN CASE WHEN $2 < 0 OR $2 & 65535 = 65535 THEN 6 ELSE $2 & 65535 END
+           THEN CASE WHEN $2 < 0 OR $2 & 0xFFFF = 0xFFFF THEN 6 ELSE $2 & 0xFFFF END
        ELSE null
   END;
 
index 8704a42b60a8fba473392984ab6c3c811d678f59..abad216b7ee41d8f2ec5c773329cb280cc5ef740 100644 (file)
@@ -527,6 +527,7 @@ T652    SQL-dynamic statements in SQL routines          NO
 T653   SQL-schema statements in external routines          YES 
 T654   SQL-dynamic statements in external routines         NO  
 T655   Cyclically dependent routines           YES 
+T661   Non-decimal integer literals            YES SQL:202x draft
 T811   Basic SQL/JSON constructor functions            NO  
 T812   SQL/JSON: JSON_OBJECTAGG            NO  
 T813   SQL/JSON: JSON_ARRAYAGG with ORDER BY           NO  
index 4014db4b80f9b2c1a1cfdd5ee9087721b35753ce..d33e3c179df7c781965ca73b986aed9fb8e8f2d9 100644 (file)
@@ -385,11 +385,46 @@ make_const(ParseState *pstate, A_Const *aconst)
            {
                /* could be an oversize integer as well as a float ... */
 
+               int         base = 10;
+               char       *startptr;
+               int         sign;
+               char       *testvalue;
                int64       val64;
                char       *endptr;
 
+               startptr = aconst->val.fval.fval;
+               if (startptr[0] == '-')
+               {
+                   sign = -1;
+                   startptr++;
+               }
+               else
+                   sign = +1;
+               if (startptr[0] == '0')
+               {
+                   if (startptr[1] == 'b' || startptr[1] == 'B')
+                   {
+                       base = 2;
+                       startptr += 2;
+                   }
+                   else if (startptr[1] == 'o' || startptr[1] == 'O')
+                   {
+                       base = 8;
+                       startptr += 2;
+                   }
+                   if (startptr[1] == 'x' || startptr[1] == 'X')
+                   {
+                       base = 16;
+                       startptr += 2;
+                   }
+               }
+
+               if (sign == +1)
+                   testvalue = startptr;
+               else
+                   testvalue = psprintf("-%s", startptr);
                errno = 0;
-               val64 = strtoi64(aconst->val.fval.fval, &endptr, 10);
+               val64 = strtoi64(testvalue, &endptr, base);
                if (errno == 0 && *endptr == '\0')
                {
                    /*
index db8b0fe8ebcc52a0d1d58e4b4615ff8bb11e68e9..9ad9e0c8ba74239b37fa465b65727ffb86728c68 100644 (file)
@@ -124,7 +124,7 @@ static void addlit(char *ytext, int yleng, core_yyscan_t yyscanner);
 static void addlitchar(unsigned char ychar, core_yyscan_t yyscanner);
 static char *litbufdup(core_yyscan_t yyscanner);
 static unsigned char unescape_single_char(unsigned char c, core_yyscan_t yyscanner);
-static int process_integer_literal(const char *token, YYSTYPE *lval);
+static int process_integer_literal(const char *token, YYSTYPE *lval, int base);
 static void addunicode(pg_wchar c, yyscan_t yyscanner);
 
 #define yyerror(msg)  scanner_yyerror(msg, yyscanner)
@@ -385,25 +385,40 @@ operator      {op_chars}+
  * Unary minus is not part of a number here.  Instead we pass it separately to
  * the parser, and there it gets coerced via doNegate().
  *
- * {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
+ * {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
  *
  * {realfail} is added to prevent the need for scanner
  * backup when the {real} rule fails to match completely.
  */
-digit          [0-9]
-
-integer            {digit}+
-decimal            (({digit}*\.{digit}+)|({digit}+\.{digit}*))
-decimalfail        {digit}+\.\.
-real           ({integer}|{decimal})[Ee][-+]?{digit}+
-realfail       ({integer}|{decimal})[Ee][-+]
-
-integer_junk   {integer}{ident_start}
-decimal_junk   {decimal}{ident_start}
+decdigit       [0-9]
+hexdigit       [0-9A-Fa-f]
+octdigit       [0-7]
+bindigit       [0-1]
+
+decinteger     {decdigit}+
+hexinteger     0[xX]{hexdigit}+
+octinteger     0[oO]{octdigit}+
+bininteger     0[bB]{bindigit}+
+
+hexfail            0[xX]
+octfail            0[oO]
+binfail            0[bB]
+
+numeric            (({decinteger}\.{decinteger}?)|(\.{decinteger}))
+numericfail        {decdigit}+\.\.
+
+real           ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
+realfail       ({decinteger}|{numeric})[Ee][-+]
+
+decinteger_junk    {decinteger}{ident_start}
+hexinteger_junk    {hexinteger}{ident_start}
+octinteger_junk    {octinteger}{ident_start}
+bininteger_junk    {bininteger}{ident_start}
+numeric_junk   {numeric}{ident_start}
 real_junk      {real}{ident_start}
 
-param          \${integer}
-param_junk     \${integer}{ident_start}
+param          \${decinteger}
+param_junk     \${decinteger}{ident_start}
 
 other          .
 
@@ -983,20 +998,44 @@ other         .
                    yyerror("trailing junk after parameter");
                }
 
-{integer}      {
+{decinteger}   {
+                   SET_YYLLOC();
+                   return process_integer_literal(yytext, yylval, 10);
+               }
+{hexinteger}   {
+                   SET_YYLLOC();
+                   return process_integer_literal(yytext, yylval, 16);
+               }
+{octinteger}   {
+                   SET_YYLLOC();
+                   return process_integer_literal(yytext, yylval, 8);
+               }
+{bininteger}   {
+                   SET_YYLLOC();
+                   return process_integer_literal(yytext, yylval, 2);
+               }
+{hexfail}      {
+                   SET_YYLLOC();
+                   yyerror("invalid hexadecimal integer");
+               }
+{octfail}      {
                    SET_YYLLOC();
-                   return process_integer_literal(yytext, yylval);
+                   yyerror("invalid octal integer");
                }
-{decimal}      {
+{binfail}      {
+                   SET_YYLLOC();
+                   yyerror("invalid binary integer");
+               }
+{numeric}      {
                    SET_YYLLOC();
                    yylval->str = pstrdup(yytext);
                    return FCONST;
                }
-{decimalfail}  {
+{numericfail}  {
                    /* throw back the .., and treat as integer */
                    yyless(yyleng - 2);
                    SET_YYLLOC();
-                   return process_integer_literal(yytext, yylval);
+                   return process_integer_literal(yytext, yylval, 10);
                }
 {real}         {
                    SET_YYLLOC();
@@ -1007,11 +1046,23 @@ other           .
                    SET_YYLLOC();
                    yyerror("trailing junk after numeric literal");
                }
-{integer_junk} {
+{decinteger_junk}  {
+                   SET_YYLLOC();
+                   yyerror("trailing junk after numeric literal");
+               }
+{hexinteger_junk}  {
+                   SET_YYLLOC();
+                   yyerror("trailing junk after numeric literal");
+               }
+{octinteger_junk}  {
+                   SET_YYLLOC();
+                   yyerror("trailing junk after numeric literal");
+               }
+{bininteger_junk}  {
                    SET_YYLLOC();
                    yyerror("trailing junk after numeric literal");
                }
-{decimal_junk} {
+{numeric_junk} {
                    SET_YYLLOC();
                    yyerror("trailing junk after numeric literal");
                }
@@ -1307,17 +1358,17 @@ litbufdup(core_yyscan_t yyscanner)
 }
 
 /*
- * Process {integer}.  Note this will also do the right thing with {decimal},
- * ie digits and a decimal point.
+ * Process {decinteger}, {hexinteger}, etc.  Note this will also do the right
+ * thing with {numeric}, ie digits and a decimal point.
  */
 static int
-process_integer_literal(const char *token, YYSTYPE *lval)
+process_integer_literal(const char *token, YYSTYPE *lval, int base)
 {
    int         val;
    char       *endptr;
 
    errno = 0;
-   val = strtoint(token, &endptr, 10);
+   val = strtoint(base == 10 ? token : token + 2, &endptr, base);
    if (*endptr != '\0' || errno == ERANGE)
    {
        /* integer too large (or contains decimal pt), treat it as a float */
index ab1564f22dabf368f64d803dd3655ae16df3f589..7cded73e6e66f6b002cf0c0e4456f347a12d28f6 100644 (file)
@@ -85,6 +85,17 @@ decimalLength64(const uint64 v)
    return t + (v >= PowersOfTen[t]);
 }
 
+static const int8 hexlookup[128] = {
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
 /*
  * Convert input string to a signed 16 bit integer.
  *
@@ -108,6 +119,7 @@ int16
 pg_strtoint16_safe(const char *s, Node *escontext)
 {
    const char *ptr = s;
+   const char *firstdigit;
    uint16      tmp = 0;
    bool        neg = false;
 
@@ -124,19 +136,60 @@ pg_strtoint16_safe(const char *s, Node *escontext)
    else if (*ptr == '+')
        ptr++;
 
-   /* require at least one digit */
-   if (unlikely(!isdigit((unsigned char) *ptr)))
-       goto invalid_syntax;
-
    /* process digits */
-   while (*ptr && isdigit((unsigned char) *ptr))
+   if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
    {
-       if (unlikely(tmp > -(PG_INT16_MIN / 10)))
-           goto out_of_range;
+       firstdigit = ptr += 2;
+
+       while (*ptr && isxdigit((unsigned char) *ptr))
+       {
+           if (unlikely(tmp > -(PG_INT16_MIN / 16)))
+               goto out_of_range;
+
+           tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+       }
+   }
+   else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
+   {
+       firstdigit = ptr += 2;
+
+       while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+       {
+           if (unlikely(tmp > -(PG_INT16_MIN / 8)))
+               goto out_of_range;
+
+           tmp = tmp * 8 + (*ptr++ - '0');
+       }
+   }
+   else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
+   {
+       firstdigit = ptr += 2;
+
+       while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+       {
+           if (unlikely(tmp > -(PG_INT16_MIN / 2)))
+               goto out_of_range;
+
+           tmp = tmp * 2 + (*ptr++ - '0');
+       }
+   }
+   else
+   {
+       firstdigit = ptr;
 
-       tmp = tmp * 10 + (*ptr++ - '0');
+       while (*ptr && isdigit((unsigned char) *ptr))
+       {
+           if (unlikely(tmp > -(PG_INT16_MIN / 10)))
+               goto out_of_range;
+
+           tmp = tmp * 10 + (*ptr++ - '0');
+       }
    }
 
+   /* require at least one digit */
+   if (unlikely(ptr == firstdigit))
+       goto invalid_syntax;
+
    /* allow trailing whitespace, but not other trailing chars */
    while (*ptr != '\0' && isspace((unsigned char) *ptr))
        ptr++;
@@ -193,6 +246,7 @@ int32
 pg_strtoint32_safe(const char *s, Node *escontext)
 {
    const char *ptr = s;
+   const char *firstdigit;
    uint32      tmp = 0;
    bool        neg = false;
 
@@ -209,19 +263,60 @@ pg_strtoint32_safe(const char *s, Node *escontext)
    else if (*ptr == '+')
        ptr++;
 
-   /* require at least one digit */
-   if (unlikely(!isdigit((unsigned char) *ptr)))
-       goto invalid_syntax;
-
    /* process digits */
-   while (*ptr && isdigit((unsigned char) *ptr))
+   if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
    {
-       if (unlikely(tmp > -(PG_INT32_MIN / 10)))
-           goto out_of_range;
+       firstdigit = ptr += 2;
+
+       while (*ptr && isxdigit((unsigned char) *ptr))
+       {
+           if (unlikely(tmp > -(PG_INT32_MIN / 16)))
+               goto out_of_range;
+
+           tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+       }
+   }
+   else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
+   {
+       firstdigit = ptr += 2;
+
+       while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+       {
+           if (unlikely(tmp > -(PG_INT32_MIN / 8)))
+               goto out_of_range;
+
+           tmp = tmp * 8 + (*ptr++ - '0');
+       }
+   }
+   else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
+   {
+       firstdigit = ptr += 2;
+
+       while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+       {
+           if (unlikely(tmp > -(PG_INT32_MIN / 2)))
+               goto out_of_range;
+
+           tmp = tmp * 2 + (*ptr++ - '0');
+       }
+   }
+   else
+   {
+       firstdigit = ptr;
+
+       while (*ptr && isdigit((unsigned char) *ptr))
+       {
+           if (unlikely(tmp > -(PG_INT32_MIN / 10)))
+               goto out_of_range;
 
-       tmp = tmp * 10 + (*ptr++ - '0');
+           tmp = tmp * 10 + (*ptr++ - '0');
+       }
    }
 
+   /* require at least one digit */
+   if (unlikely(ptr == firstdigit))
+       goto invalid_syntax;
+
    /* allow trailing whitespace, but not other trailing chars */
    while (*ptr != '\0' && isspace((unsigned char) *ptr))
        ptr++;
@@ -278,6 +373,7 @@ int64
 pg_strtoint64_safe(const char *s, Node *escontext)
 {
    const char *ptr = s;
+   const char *firstdigit;
    uint64      tmp = 0;
    bool        neg = false;
 
@@ -294,18 +390,59 @@ pg_strtoint64_safe(const char *s, Node *escontext)
    else if (*ptr == '+')
        ptr++;
 
-   /* require at least one digit */
-   if (unlikely(!isdigit((unsigned char) *ptr)))
-       goto invalid_syntax;
-
    /* process digits */
-   while (*ptr && isdigit((unsigned char) *ptr))
+   if (ptr[0] == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
    {
-       if (unlikely(tmp > -(PG_INT64_MIN / 10)))
-           goto out_of_range;
+       firstdigit = ptr += 2;
+
+       while (*ptr && isxdigit((unsigned char) *ptr))
+       {
+           if (unlikely(tmp > -(PG_INT64_MIN / 16)))
+               goto out_of_range;
 
-       tmp = tmp * 10 + (*ptr++ - '0');
+           tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+       }
    }
+   else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
+   {
+       firstdigit = ptr += 2;
+
+       while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+       {
+           if (unlikely(tmp > -(PG_INT64_MIN / 8)))
+               goto out_of_range;
+
+           tmp = tmp * 8 + (*ptr++ - '0');
+       }
+   }
+   else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
+   {
+       firstdigit = ptr += 2;
+
+       while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+       {
+           if (unlikely(tmp > -(PG_INT64_MIN / 2)))
+               goto out_of_range;
+
+           tmp = tmp * 2 + (*ptr++ - '0');
+       }
+   }
+   else
+   {
+       firstdigit = ptr;
+
+       while (*ptr && isdigit((unsigned char) *ptr))
+       {
+           if (unlikely(tmp > -(PG_INT64_MIN / 10)))
+               goto out_of_range;
+
+           tmp = tmp * 10 + (*ptr++ - '0');
+       }
+   }
+
+   /* require at least one digit */
+   if (unlikely(ptr == firstdigit))
+       goto invalid_syntax;
 
    /* allow trailing whitespace, but not other trailing chars */
    while (*ptr != '\0' && isspace((unsigned char) *ptr))
index ae531ec2407793135b85590ae27e0fb5d0040088..cb1fc5213844ca80baca79123315e51984689dc3 100644 (file)
@@ -323,25 +323,40 @@ operator      {op_chars}+
  * Unary minus is not part of a number here.  Instead we pass it separately to
  * the parser, and there it gets coerced via doNegate().
  *
- * {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
+ * {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
  *
  * {realfail} is added to prevent the need for scanner
  * backup when the {real} rule fails to match completely.
  */
-digit          [0-9]
-
-integer            {digit}+
-decimal            (({digit}*\.{digit}+)|({digit}+\.{digit}*))
-decimalfail        {digit}+\.\.
-real           ({integer}|{decimal})[Ee][-+]?{digit}+
-realfail       ({integer}|{decimal})[Ee][-+]
-
-integer_junk   {integer}{ident_start}
-decimal_junk   {decimal}{ident_start}
+decdigit       [0-9]
+hexdigit       [0-9A-Fa-f]
+octdigit       [0-7]
+bindigit       [0-1]
+
+decinteger     {decdigit}+
+hexinteger     0[xX]{hexdigit}+
+octinteger     0[oO]{octdigit}+
+bininteger     0[bB]{bindigit}+
+
+hexfail            0[xX]
+octfail            0[oO]
+binfail            0[bB]
+
+numeric            (({decinteger}\.{decinteger}?)|(\.{decinteger}))
+numericfail        {decdigit}+\.\.
+
+real           ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
+realfail       ({decinteger}|{numeric})[Ee][-+]
+
+decinteger_junk    {decinteger}{ident_start}
+hexinteger_junk    {hexinteger}{ident_start}
+octinteger_junk    {octinteger}{ident_start}
+bininteger_junk    {bininteger}{ident_start}
+numeric_junk   {numeric}{ident_start}
 real_junk      {real}{ident_start}
 
-param          \${integer}
-param_junk     \${integer}{ident_start}
+param          \${decinteger}
+param_junk     \${decinteger}{ident_start}
 
 /* psql-specific: characters allowed in variable names */
 variable_char  [A-Za-z\200-\377_0-9]
@@ -847,13 +862,31 @@ other         .
                    ECHO;
                }
 
-{integer}      {
+{decinteger}   {
+                   ECHO;
+               }
+{hexinteger}   {
+                   ECHO;
+               }
+{octinteger}   {
+                   ECHO;
+               }
+{bininteger}   {
+                   ECHO;
+               }
+{hexfail}      {
                    ECHO;
                }
-{decimal}      {
+{octfail}      {
                    ECHO;
                }
-{decimalfail}  {
+{binfail}      {
+                   ECHO;
+               }
+{numeric}      {
+                   ECHO;
+               }
+{numericfail}  {
                    /* throw back the .., and treat as integer */
                    yyless(yyleng - 2);
                    ECHO;
@@ -864,10 +897,19 @@ other         .
 {realfail}     {
                    ECHO;
                }
-{integer_junk} {
+{decinteger_junk}  {
+                   ECHO;
+               }
+{hexinteger_junk}  {
+                   ECHO;
+               }
+{octinteger_junk}  {
+                   ECHO;
+               }
+{bininteger_junk}  {
                    ECHO;
                }
-{decimal_junk} {
+{numeric_junk} {
                    ECHO;
                }
 {real_junk}        {
index c145c9698f1a064da5bd8dda100bd51b57f0d144..2c09c6cb4f356d8c8f060951c23febed21c295f3 100644 (file)
@@ -57,7 +57,7 @@ static bool       include_next;
 #define startlit() (literalbuf[0] = '\0', literallen = 0)
 static void addlit(char *ytext, int yleng);
 static void addlitchar(unsigned char ychar);
-static int process_integer_literal(const char *token, YYSTYPE *lval);
+static int process_integer_literal(const char *token, YYSTYPE *lval, int base);
 static void parse_include(void);
 static bool ecpg_isspace(char ch);
 static bool isdefine(void);
@@ -351,25 +351,40 @@ operator      {op_chars}+
  * Unary minus is not part of a number here.  Instead we pass it separately to
  * the parser, and there it gets coerced via doNegate().
  *
- * {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
+ * {numericfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
  *
  * {realfail} is added to prevent the need for scanner
  * backup when the {real} rule fails to match completely.
  */
-digit          [0-9]
-
-integer            {digit}+
-decimal            (({digit}*\.{digit}+)|({digit}+\.{digit}*))
-decimalfail        {digit}+\.\.
-real           ({integer}|{decimal})[Ee][-+]?{digit}+
-realfail       ({integer}|{decimal})[Ee][-+]
-
-integer_junk   {integer}{ident_start}
-decimal_junk   {decimal}{ident_start}
+decdigit       [0-9]
+hexdigit       [0-9A-Fa-f]
+octdigit       [0-7]
+bindigit       [0-1]
+
+decinteger     {decdigit}+
+hexinteger     0[xX]{hexdigit}+
+octinteger     0[oO]{octdigit}+
+bininteger     0[bB]{bindigit}+
+
+hexfail            0[xX]
+octfail            0[oO]
+binfail            0[bB]
+
+numeric            (({decinteger}\.{decinteger}?)|(\.{decinteger}))
+numericfail        {decdigit}+\.\.
+
+real           ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
+realfail       ({decinteger}|{numeric})[Ee][-+]
+
+decinteger_junk    {decinteger}{ident_start}
+hexinteger_junk    {hexinteger}{ident_start}
+octinteger_junk    {octinteger}{ident_start}
+bininteger_junk    {bininteger}{ident_start}
+numeric_junk   {numeric}{ident_start}
 real_junk      {real}{ident_start}
 
-param          \${integer}
-param_junk     \${integer}{ident_start}
+param          \${decinteger}
+param_junk     \${decinteger}{ident_start}
 
 /* special characters for other dbms */
 /* we have to react differently in compat mode */
@@ -399,9 +414,6 @@ include_next    [iI][nN][cC][lL][uU][dD][eE]_[nN][eE][xX][tT]
 import         [iI][mM][pP][oO][rR][tT]
 undef          [uU][nN][dD][eE][fF]
 
-/* C version of hex number */
-xch                0[xX][0-9A-Fa-f]*
-
 ccomment       "//".*\n
 
 if             [iI][fF]
@@ -414,7 +426,7 @@ endif           [eE][nN][dD][iI][fF]
 struct         [sS][tT][rR][uU][cC][tT]
 
 exec_sql       {exec}{space}*{sql}{space}*
-ipdigit            ({digit}|{digit}{digit}|{digit}{digit}{digit})
+ipdigit            ({decdigit}|{decdigit}{decdigit}|{decdigit}{decdigit}{decdigit})
 ip             {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
 
 /* we might want to parse all cpp include files */
@@ -932,17 +944,20 @@ cppline           {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
 }  /* <SQL> */
 
 <C,SQL>{
-{integer}      {
-                   return process_integer_literal(yytext, &base_yylval);
+{decinteger}   {
+                   return process_integer_literal(yytext, &base_yylval, 10);
                }
-{decimal}      {
+{hexinteger}   {
+                   return process_integer_literal(yytext, &base_yylval, 16);
+               }
+{numeric}      {
                    base_yylval.str = mm_strdup(yytext);
                    return FCONST;
                }
-{decimalfail}  {
+{numericfail}  {
                    /* throw back the .., and treat as integer */
                    yyless(yyleng - 2);
-                   return process_integer_literal(yytext, &base_yylval);
+                   return process_integer_literal(yytext, &base_yylval, 10);
                }
 {real}         {
                    base_yylval.str = mm_strdup(yytext);
@@ -951,22 +966,38 @@ cppline           {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
 {realfail}     {
                    /*
                     * throw back the [Ee][+-], and figure out whether what
-                    * remains is an {integer} or {decimal}.
+                    * remains is an {decinteger} or {numeric}.
                     */
                    yyless(yyleng - 2);
-                   return process_integer_literal(yytext, &base_yylval);
+                   return process_integer_literal(yytext, &base_yylval, 10);
                }
 } /* <C,SQL> */
 
 <SQL>{
+{octinteger}   {
+                   return process_integer_literal(yytext, &base_yylval, 8);
+               }
+{bininteger}   {
+                   return process_integer_literal(yytext, &base_yylval, 2);
+               }
+
    /*
     * Note that some trailing junk is valid in C (such as 100LL), so we
     * contain this to SQL mode.
     */
-{integer_junk} {
+{decinteger_junk}  {
                    mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
                }
-{decimal_junk} {
+{hexinteger_junk}  {
+                   mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
+               }
+{octinteger_junk}  {
+                   mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
+               }
+{bininteger_junk}  {
+                   mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
+               }
+{numeric_junk} {
                    mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
                }
 {real_junk}        {
@@ -1036,19 +1067,6 @@ cppline          {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                            return S_ANYTHING;
                     }
 <C>{ccomment}      { ECHO; }
-<C>{xch}           {
-                       char* endptr;
-
-                       errno = 0;
-                       base_yylval.ival = strtoul((char *) yytext, &endptr, 16);
-                       if (*endptr != '\0' || errno == ERANGE)
-                       {
-                           errno = 0;
-                           base_yylval.str = mm_strdup(yytext);
-                           return SCONST;
-                       }
-                       return ICONST;
-                   }
 <C>{cppinclude}        {
                        if (system_includes)
                        {
@@ -1573,17 +1591,17 @@ addlitchar(unsigned char ychar)
 }
 
 /*
- * Process {integer}.  Note this will also do the right thing with {decimal},
- * ie digits and a decimal point.
+ * Process {decinteger}, {hexinteger}, etc.  Note this will also do the right
+ * thing with {numeric}, ie digits and a decimal point.
  */
 static int
-process_integer_literal(const char *token, YYSTYPE *lval)
+process_integer_literal(const char *token, YYSTYPE *lval, int base)
 {
    int         val;
    char       *endptr;
 
    errno = 0;
-   val = strtoint(token, &endptr, 10);
+   val = strtoint(base == 10 ? token : token + 2, &endptr, base);
    if (*endptr != '\0' || errno == ERANGE)
    {
        /* integer too large (or contains decimal pt), treat it as a float */
index 6a23567b6796365f78e00fb8e7da7db517b2a622..08e2f9f9dca372b685d9437a0a62b58865eefd59 100644 (file)
@@ -329,3 +329,95 @@ FROM (VALUES (-2.5::numeric),
   2.5 |          3
 (7 rows)
 
+-- non-decimal literals
+SELECT int2 '0b100101';
+ int2 
+------
+   37
+(1 row)
+
+SELECT int2 '0o273';
+ int2 
+------
+  187
+(1 row)
+
+SELECT int2 '0x42F';
+ int2 
+------
+ 1071
+(1 row)
+
+SELECT int2 '0b';
+ERROR:  invalid input syntax for type smallint: "0b"
+LINE 1: SELECT int2 '0b';
+                    ^
+SELECT int2 '0o';
+ERROR:  invalid input syntax for type smallint: "0o"
+LINE 1: SELECT int2 '0o';
+                    ^
+SELECT int2 '0x';
+ERROR:  invalid input syntax for type smallint: "0x"
+LINE 1: SELECT int2 '0x';
+                    ^
+-- cases near overflow
+SELECT int2 '0b111111111111111';
+ int2  
+-------
+ 32767
+(1 row)
+
+SELECT int2 '0b1000000000000000';
+ERROR:  value "0b1000000000000000" is out of range for type smallint
+LINE 1: SELECT int2 '0b1000000000000000';
+                    ^
+SELECT int2 '0o77777';
+ int2  
+-------
+ 32767
+(1 row)
+
+SELECT int2 '0o100000';
+ERROR:  value "0o100000" is out of range for type smallint
+LINE 1: SELECT int2 '0o100000';
+                    ^
+SELECT int2 '0x7FFF';
+ int2  
+-------
+ 32767
+(1 row)
+
+SELECT int2 '0x8000';
+ERROR:  value "0x8000" is out of range for type smallint
+LINE 1: SELECT int2 '0x8000';
+                    ^
+SELECT int2 '-0b1000000000000000';
+  int2  
+--------
+ -32768
+(1 row)
+
+SELECT int2 '-0b1000000000000001';
+ERROR:  value "-0b1000000000000001" is out of range for type smallint
+LINE 1: SELECT int2 '-0b1000000000000001';
+                    ^
+SELECT int2 '-0o100000';
+  int2  
+--------
+ -32768
+(1 row)
+
+SELECT int2 '-0o100001';
+ERROR:  value "-0o100001" is out of range for type smallint
+LINE 1: SELECT int2 '-0o100001';
+                    ^
+SELECT int2 '-0x8000';
+  int2  
+--------
+ -32768
+(1 row)
+
+SELECT int2 '-0x8001';
+ERROR:  value "-0x8001" is out of range for type smallint
+LINE 1: SELECT int2 '-0x8001';
+                    ^
index b98007bd7a2f1b73b2825287b36eafc310bdf717..8386c7cdff19358e729ebd42fc0fa33566831a64 100644 (file)
@@ -456,3 +456,95 @@ SELECT lcm((-2147483648)::int4, 1::int4); -- overflow
 ERROR:  integer out of range
 SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow
 ERROR:  integer out of range
+-- non-decimal literals
+SELECT int4 '0b100101';
+ int4 
+------
+   37
+(1 row)
+
+SELECT int4 '0o273';
+ int4 
+------
+  187
+(1 row)
+
+SELECT int4 '0x42F';
+ int4 
+------
+ 1071
+(1 row)
+
+SELECT int4 '0b';
+ERROR:  invalid input syntax for type integer: "0b"
+LINE 1: SELECT int4 '0b';
+                    ^
+SELECT int4 '0o';
+ERROR:  invalid input syntax for type integer: "0o"
+LINE 1: SELECT int4 '0o';
+                    ^
+SELECT int4 '0x';
+ERROR:  invalid input syntax for type integer: "0x"
+LINE 1: SELECT int4 '0x';
+                    ^
+-- cases near overflow
+SELECT int4 '0b1111111111111111111111111111111';
+    int4    
+------------
+ 2147483647
+(1 row)
+
+SELECT int4 '0b10000000000000000000000000000000';
+ERROR:  value "0b10000000000000000000000000000000" is out of range for type integer
+LINE 1: SELECT int4 '0b10000000000000000000000000000000';
+                    ^
+SELECT int4 '0o17777777777';
+    int4    
+------------
+ 2147483647
+(1 row)
+
+SELECT int4 '0o20000000000';
+ERROR:  value "0o20000000000" is out of range for type integer
+LINE 1: SELECT int4 '0o20000000000';
+                    ^
+SELECT int4 '0x7FFFFFFF';
+    int4    
+------------
+ 2147483647
+(1 row)
+
+SELECT int4 '0x80000000';
+ERROR:  value "0x80000000" is out of range for type integer
+LINE 1: SELECT int4 '0x80000000';
+                    ^
+SELECT int4 '-0b10000000000000000000000000000000';
+    int4     
+-------------
+ -2147483648
+(1 row)
+
+SELECT int4 '-0b10000000000000000000000000000001';
+ERROR:  value "-0b10000000000000000000000000000001" is out of range for type integer
+LINE 1: SELECT int4 '-0b10000000000000000000000000000001';
+                    ^
+SELECT int4 '-0o20000000000';
+    int4     
+-------------
+ -2147483648
+(1 row)
+
+SELECT int4 '-0o20000000001';
+ERROR:  value "-0o20000000001" is out of range for type integer
+LINE 1: SELECT int4 '-0o20000000001';
+                    ^
+SELECT int4 '-0x80000000';
+    int4     
+-------------
+ -2147483648
+(1 row)
+
+SELECT int4 '-0x80000001';
+ERROR:  value "-0x80000001" is out of range for type integer
+LINE 1: SELECT int4 '-0x80000001';
+                    ^
index 90ed06124986a6596aeaa1aefa1b4a89b68d3e04..5b62b51be9c91e8231873ce094dbdbce57cd2a23 100644 (file)
@@ -952,3 +952,95 @@ SELECT lcm((-9223372036854775808)::int8, 1::int8); -- overflow
 ERROR:  bigint out of range
 SELECT lcm(9223372036854775807::int8, 9223372036854775806::int8); -- overflow
 ERROR:  bigint out of range
+-- non-decimal literals
+SELECT int8 '0b100101';
+ int8 
+------
+   37
+(1 row)
+
+SELECT int8 '0o273';
+ int8 
+------
+  187
+(1 row)
+
+SELECT int8 '0x42F';
+ int8 
+------
+ 1071
+(1 row)
+
+SELECT int8 '0b';
+ERROR:  invalid input syntax for type bigint: "0b"
+LINE 1: SELECT int8 '0b';
+                    ^
+SELECT int8 '0o';
+ERROR:  invalid input syntax for type bigint: "0o"
+LINE 1: SELECT int8 '0o';
+                    ^
+SELECT int8 '0x';
+ERROR:  invalid input syntax for type bigint: "0x"
+LINE 1: SELECT int8 '0x';
+                    ^
+-- cases near overflow
+SELECT int8 '0b111111111111111111111111111111111111111111111111111111111111111';
+        int8         
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT int8 '0b1000000000000000000000000000000000000000000000000000000000000000';
+ERROR:  value "0b1000000000000000000000000000000000000000000000000000000000000000" is out of range for type bigint
+LINE 1: SELECT int8 '0b100000000000000000000000000000000000000000000...
+                    ^
+SELECT int8 '0o777777777777777777777';
+        int8         
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT int8 '0o1000000000000000000000';
+ERROR:  value "0o1000000000000000000000" is out of range for type bigint
+LINE 1: SELECT int8 '0o1000000000000000000000';
+                    ^
+SELECT int8 '0x7FFFFFFFFFFFFFFF';
+        int8         
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT int8 '0x8000000000000000';
+ERROR:  value "0x8000000000000000" is out of range for type bigint
+LINE 1: SELECT int8 '0x8000000000000000';
+                    ^
+SELECT int8 '-0b1000000000000000000000000000000000000000000000000000000000000000';
+         int8         
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT int8 '-0b1000000000000000000000000000000000000000000000000000000000000001';
+ERROR:  value "-0b1000000000000000000000000000000000000000000000000000000000000001" is out of range for type bigint
+LINE 1: SELECT int8 '-0b10000000000000000000000000000000000000000000...
+                    ^
+SELECT int8 '-0o1000000000000000000000';
+         int8         
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT int8 '-0o1000000000000000000001';
+ERROR:  value "-0o1000000000000000000001" is out of range for type bigint
+LINE 1: SELECT int8 '-0o1000000000000000000001';
+                    ^
+SELECT int8 '-0x8000000000000000';
+         int8         
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT int8 '-0x8000000000000001';
+ERROR:  value "-0x8000000000000001" is out of range for type bigint
+LINE 1: SELECT int8 '-0x8000000000000001';
+                    ^
index 77d48434173bb6991d731c89a7a0e190feac2b1c..15cd6b167236f86131648d1c60e3c859c5cd66ca 100644 (file)
 -- Test various combinations of numeric types and functions.
 --
 --
--- Trailing junk in numeric literals
+-- numeric literals
 --
+SELECT 0b100101;
+ ?column? 
+----------
+       37
+(1 row)
+
+SELECT 0o273;
+ ?column? 
+----------
+      187
+(1 row)
+
+SELECT 0x42F;
+ ?column? 
+----------
+     1071
+(1 row)
+
+-- cases near int4 overflow
+SELECT 0b1111111111111111111111111111111;
+  ?column?  
+------------
+ 2147483647
+(1 row)
+
+SELECT 0b10000000000000000000000000000000;
+  ?column?  
+------------
+ 2147483648
+(1 row)
+
+SELECT 0o17777777777;
+  ?column?  
+------------
+ 2147483647
+(1 row)
+
+SELECT 0o20000000000;
+  ?column?  
+------------
+ 2147483648
+(1 row)
+
+SELECT 0x7FFFFFFF;
+  ?column?  
+------------
+ 2147483647
+(1 row)
+
+SELECT 0x80000000;
+  ?column?  
+------------
+ 2147483648
+(1 row)
+
+SELECT -0b10000000000000000000000000000000;
+  ?column?   
+-------------
+ -2147483648
+(1 row)
+
+SELECT -0b10000000000000000000000000000001;
+  ?column?   
+-------------
+ -2147483649
+(1 row)
+
+SELECT -0o20000000000;
+  ?column?   
+-------------
+ -2147483648
+(1 row)
+
+SELECT -0o20000000001;
+  ?column?   
+-------------
+ -2147483649
+(1 row)
+
+SELECT -0x80000000;
+  ?column?   
+-------------
+ -2147483648
+(1 row)
+
+SELECT -0x80000001;
+  ?column?   
+-------------
+ -2147483649
+(1 row)
+
+-- cases near int8 overflow
+SELECT 0b111111111111111111111111111111111111111111111111111111111111111;
+      ?column?       
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT 0b1000000000000000000000000000000000000000000000000000000000000000;
+ERROR:  invalid input syntax for type numeric: "0b1000000000000000000000000000000000000000000000000000000000000000"
+LINE 1: SELECT 0b100000000000000000000000000000000000000000000000000...
+               ^
+SELECT 0o777777777777777777777;
+      ?column?       
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT 0o1000000000000000000000;
+ERROR:  invalid input syntax for type numeric: "0o1000000000000000000000"
+LINE 1: SELECT 0o1000000000000000000000;
+               ^
+SELECT 0x7FFFFFFFFFFFFFFF;
+      ?column?       
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT 0x8000000000000000;
+ERROR:  invalid input syntax for type numeric: "0x8000000000000000"
+LINE 1: SELECT 0x8000000000000000;
+               ^
+SELECT -0b1000000000000000000000000000000000000000000000000000000000000000;
+       ?column?       
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT -0b1000000000000000000000000000000000000000000000000000000000000001;
+ERROR:  invalid input syntax for type numeric: "-0b1000000000000000000000000000000000000000000000000000000000000001"
+LINE 1: SELECT -0b10000000000000000000000000000000000000000000000000...
+               ^
+SELECT -0o1000000000000000000000;
+       ?column?       
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT -0o1000000000000000000001;
+ERROR:  invalid input syntax for type numeric: "-0o1000000000000000000001"
+LINE 1: SELECT -0o1000000000000000000001;
+               ^
+SELECT -0x8000000000000000;
+       ?column?       
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT -0x8000000000000001;
+ERROR:  invalid input syntax for type numeric: "-0x8000000000000001"
+LINE 1: SELECT -0x8000000000000001;
+               ^
+-- error cases
 SELECT 123abc;
 ERROR:  trailing junk after numeric literal at or near "123a"
 LINE 1: SELECT 123abc;
                ^
 SELECT 0x0o;
-ERROR:  trailing junk after numeric literal at or near "0x"
+ERROR:  trailing junk after numeric literal at or near "0x0o"
 LINE 1: SELECT 0x0o;
                ^
 SELECT 1_2_3;
@@ -45,6 +198,42 @@ PREPARE p1 AS SELECT $1a;
 ERROR:  trailing junk after parameter at or near "$1a"
 LINE 1: PREPARE p1 AS SELECT $1a;
                              ^
+SELECT 0b;
+ERROR:  invalid binary integer at or near "0b"
+LINE 1: SELECT 0b;
+               ^
+SELECT 1b;
+ERROR:  trailing junk after numeric literal at or near "1b"
+LINE 1: SELECT 1b;
+               ^
+SELECT 0b0x;
+ERROR:  trailing junk after numeric literal at or near "0b0x"
+LINE 1: SELECT 0b0x;
+               ^
+SELECT 0o;
+ERROR:  invalid octal integer at or near "0o"
+LINE 1: SELECT 0o;
+               ^
+SELECT 1o;
+ERROR:  trailing junk after numeric literal at or near "1o"
+LINE 1: SELECT 1o;
+               ^
+SELECT 0o0x;
+ERROR:  trailing junk after numeric literal at or near "0o0x"
+LINE 1: SELECT 0o0x;
+               ^
+SELECT 0x;
+ERROR:  invalid hexadecimal integer at or near "0x"
+LINE 1: SELECT 0x;
+               ^
+SELECT 1x;
+ERROR:  trailing junk after numeric literal at or near "1x"
+LINE 1: SELECT 1x;
+               ^
+SELECT 0x0y;
+ERROR:  trailing junk after numeric literal at or near "0x0y"
+LINE 1: SELECT 0x0y;
+               ^
 --
 -- Test implicit type conversions
 -- This fails for Postgres v6.1 (and earlier?)
index 98a761a24a361a29d34f30ea7f4ccea603f388e1..ad30c2feaa110b6651b70ba2733841797e60fe1b 100644 (file)
@@ -110,3 +110,29 @@ FROM (VALUES (-2.5::numeric),
              (0.5::numeric),
              (1.5::numeric),
              (2.5::numeric)) t(x);
+
+
+-- non-decimal literals
+
+SELECT int2 '0b100101';
+SELECT int2 '0o273';
+SELECT int2 '0x42F';
+
+SELECT int2 '0b';
+SELECT int2 '0o';
+SELECT int2 '0x';
+
+-- cases near overflow
+SELECT int2 '0b111111111111111';
+SELECT int2 '0b1000000000000000';
+SELECT int2 '0o77777';
+SELECT int2 '0o100000';
+SELECT int2 '0x7FFF';
+SELECT int2 '0x8000';
+
+SELECT int2 '-0b1000000000000000';
+SELECT int2 '-0b1000000000000001';
+SELECT int2 '-0o100000';
+SELECT int2 '-0o100001';
+SELECT int2 '-0x8000';
+SELECT int2 '-0x8001';
index 54420818de5680ff298a69d9f2c0fbf43ae51ea1..9e6a40408ab87f0aef4382af0c7657da9726eab7 100644 (file)
@@ -170,3 +170,29 @@ FROM (VALUES (0::int4, 0::int4),
 
 SELECT lcm((-2147483648)::int4, 1::int4); -- overflow
 SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow
+
+
+-- non-decimal literals
+
+SELECT int4 '0b100101';
+SELECT int4 '0o273';
+SELECT int4 '0x42F';
+
+SELECT int4 '0b';
+SELECT int4 '0o';
+SELECT int4 '0x';
+
+-- cases near overflow
+SELECT int4 '0b1111111111111111111111111111111';
+SELECT int4 '0b10000000000000000000000000000000';
+SELECT int4 '0o17777777777';
+SELECT int4 '0o20000000000';
+SELECT int4 '0x7FFFFFFF';
+SELECT int4 '0x80000000';
+
+SELECT int4 '-0b10000000000000000000000000000000';
+SELECT int4 '-0b10000000000000000000000000000001';
+SELECT int4 '-0o20000000000';
+SELECT int4 '-0o20000000001';
+SELECT int4 '-0x80000000';
+SELECT int4 '-0x80000001';
index 76007b692b2bb6fa9d3d5aa1fc052374de0649b4..06f273ed5845cedb481876e9f78befb0bdfb304e 100644 (file)
@@ -251,3 +251,29 @@ FROM (VALUES (0::int8, 0::int8),
 
 SELECT lcm((-9223372036854775808)::int8, 1::int8); -- overflow
 SELECT lcm(9223372036854775807::int8, 9223372036854775806::int8); -- overflow
+
+
+-- non-decimal literals
+
+SELECT int8 '0b100101';
+SELECT int8 '0o273';
+SELECT int8 '0x42F';
+
+SELECT int8 '0b';
+SELECT int8 '0o';
+SELECT int8 '0x';
+
+-- cases near overflow
+SELECT int8 '0b111111111111111111111111111111111111111111111111111111111111111';
+SELECT int8 '0b1000000000000000000000000000000000000000000000000000000000000000';
+SELECT int8 '0o777777777777777777777';
+SELECT int8 '0o1000000000000000000000';
+SELECT int8 '0x7FFFFFFFFFFFFFFF';
+SELECT int8 '0x8000000000000000';
+
+SELECT int8 '-0b1000000000000000000000000000000000000000000000000000000000000000';
+SELECT int8 '-0b1000000000000000000000000000000000000000000000000000000000000001';
+SELECT int8 '-0o1000000000000000000000';
+SELECT int8 '-0o1000000000000000000001';
+SELECT int8 '-0x8000000000000000';
+SELECT int8 '-0x8000000000000001';
index be7d6dfe0c2676012205fc9ee6ff1d9daaec1257..310d9e57663eb54366ef3b533477a52af7b52d31 100644 (file)
@@ -3,10 +3,46 @@
 -- Test various combinations of numeric types and functions.
 --
 
+
 --
--- Trailing junk in numeric literals
+-- numeric literals
 --
 
+SELECT 0b100101;
+SELECT 0o273;
+SELECT 0x42F;
+
+-- cases near int4 overflow
+SELECT 0b1111111111111111111111111111111;
+SELECT 0b10000000000000000000000000000000;
+SELECT 0o17777777777;
+SELECT 0o20000000000;
+SELECT 0x7FFFFFFF;
+SELECT 0x80000000;
+
+SELECT -0b10000000000000000000000000000000;
+SELECT -0b10000000000000000000000000000001;
+SELECT -0o20000000000;
+SELECT -0o20000000001;
+SELECT -0x80000000;
+SELECT -0x80000001;
+
+-- cases near int8 overflow
+SELECT 0b111111111111111111111111111111111111111111111111111111111111111;
+SELECT 0b1000000000000000000000000000000000000000000000000000000000000000;
+SELECT 0o777777777777777777777;
+SELECT 0o1000000000000000000000;
+SELECT 0x7FFFFFFFFFFFFFFF;
+SELECT 0x8000000000000000;
+
+SELECT -0b1000000000000000000000000000000000000000000000000000000000000000;
+SELECT -0b1000000000000000000000000000000000000000000000000000000000000001;
+SELECT -0o1000000000000000000000;
+SELECT -0o1000000000000000000001;
+SELECT -0x8000000000000000;
+SELECT -0x8000000000000001;
+
+-- error cases
 SELECT 123abc;
 SELECT 0x0o;
 SELECT 1_2_3;
@@ -18,6 +54,19 @@ SELECT 0.0e;
 SELECT 0.0e+a;
 PREPARE p1 AS SELECT $1a;
 
+SELECT 0b;
+SELECT 1b;
+SELECT 0b0x;
+
+SELECT 0o;
+SELECT 1o;
+SELECT 0o0x;
+
+SELECT 0x;
+SELECT 1x;
+SELECT 0x0y;
+
+
 --
 -- Test implicit type conversions
 -- This fails for Postgres v6.1 (and earlier?)