Allow underscores in integer and numeric constants.
authorDean Rasheed
Sat, 4 Feb 2023 09:48:51 +0000 (09:48 +0000)
committerDean Rasheed
Sat, 4 Feb 2023 09:48:51 +0000 (09:48 +0000)
This allows underscores to be used in integer and numeric literals,
and their corresponding type input functions, for visual grouping.
For example:

    1_500_000_000
    3.14159_26535_89793
    0xffff_ffff
    0b_1001_0001

A single underscore is allowed between any 2 digits, or immediately
after the base prefix indicator of non-decimal integers, per SQL:202x
draft.

Peter Eisentraut and Dean Rasheed

Discussion: https://postgr.es/m/84aae844-dc55-a4be-86d9-4f0fa405cc97%40enterprisedb.com

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

index 0ccddea310dab67463657395041ada28bc8efa04..5668ab01433e3cbadb4f5f87f79b5e2e0a9b66e1 100644 (file)
@@ -677,7 +677,8 @@ $function$
      decimal point, if one is used.  At least one digit must follow the
      exponent marker (e), if one is present.
      There cannot be any spaces or other characters embedded in the
-     constant.  Note that any leading plus or minus sign is not actually
+     constant, except for underscores, which can be used for visual grouping as
+     described below.  Note that any leading plus or minus sign is not actually
      considered part of the constant; it is an operator applied to the
      constant.
     
@@ -695,23 +696,24 @@ $function$
     
 
     
-     Additionally, non-decimal integer constants can be used in these forms:
+     Additionally, non-decimal integer constants are accepted in these forms:
 
 0xhexdigits
 0ooctdigits
 0bbindigits
 
-     hexdigits is one or more hexadecimal digits
+     where hexdigits is one or more hexadecimal digits
      (0-9, A-F), octdigits is one or more octal
-     digits (0-7), bindigits is one or more binary
+     digits (0-7), and bindigits 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.
     
 
     
-     These are some examples of this:
-0b100101
+     These are some examples of valid non-decimal integer constants:
+
+0b100101
 0B10011001
 0o273
 0O755
@@ -720,13 +722,21 @@ $function$
 
     
 
-    
-     
-      Non-decimal integer constants are currently only supported in the range
-      of the bigint type (see 
-      linkend="datatype-numeric-table"/>).
-     
-    
+    
+     For visual grouping, underscores can be inserted between digits.  These
+     have no further effect on the value of the constant.  For example:
+
+1_500_000_000
+0b10001000_00000000
+0o_1_755
+0xFFFF_FFFF
+1.618_034
+
+     Underscores are not allowed at the start or end of a numeric constant or
+     a group of digits (that is, immediately before or after the decimal point
+     or the exponent marker), and more than one underscore in a row is not
+     allowed.
+    
 
     
      integer
index abad216b7ee41d8f2ec5c773329cb280cc5ef740..3766762ae361afa32874382e633f5a55b72b5ce8 100644 (file)
@@ -528,6 +528,7 @@ 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
+T662   Underscores in 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 f1967a33bc0be0783afe12dfaaf4a371c6a34797..5020b9f081015ff84906898fbfd75997a56e7b10 100644 (file)
@@ -19,6 +19,7 @@
 #include "catalog/pg_type.h"
 #include "mb/pg_wchar.h"
 #include "nodes/makefuncs.h"
+#include "nodes/miscnodes.h"
 #include "nodes/nodeFuncs.h"
 #include "nodes/subscripting.h"
 #include "parser/parse_coerce.h"
@@ -385,47 +386,11 @@ 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;
+               ErrorSaveContext escontext = {T_ErrorSaveContext};
                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;
-                   }
-                   else 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(testvalue, &endptr, base);
-               if (errno == 0 && *endptr == '\0')
+               val64 = pg_strtoint64_safe(aconst->val.fval.fval, (Node *) &escontext);
+               if (!escontext.error_occurred)
                {
                    /*
                     * It might actually fit in int32. Probably only INT_MIN
index 1e821d4c9e2752c20c8595f70ca89c8781ff9fe7..b2216a9eacd101e067f48ad5df679c55edf4c7b5 100644 (file)
 
 #include "common/string.h"
 #include "gramparse.h"
+#include "nodes/miscnodes.h"
 #include "parser/parser.h"     /* only needed for GUC variables */
 #include "parser/scansup.h"
 #include "port/pg_bitutils.h"
 #include "mb/pg_wchar.h"
+#include "utils/builtins.h"
 }
 
 %{
@@ -395,19 +397,19 @@ 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}+
+decinteger     {decdigit}(_?{decdigit})*
+hexinteger     0[xX](_?{hexdigit})+
+octinteger     0[oO](_?{octdigit})+
+bininteger     0[bB](_?{bindigit})+
 
-hexfail            0[xX]
-octfail            0[oO]
-binfail            0[bB]
+hexfail            0[xX]_?
+octfail            0[oO]_?
+binfail            0[bB]_?
 
 numeric            (({decinteger}\.{decinteger}?)|(\.{decinteger}))
 numericfail        {decdigit}+\.\.
 
-real           ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
+real           ({decinteger}|{numeric})[Ee][-+]?{decinteger}
 realfail       ({decinteger}|{numeric})[Ee][-+]
 
 decinteger_junk    {decinteger}{ident_start}
@@ -1364,12 +1366,11 @@ litbufdup(core_yyscan_t yyscanner)
 static int
 process_integer_literal(const char *token, YYSTYPE *lval, int base)
 {
-   int         val;
-   char       *endptr;
+   ErrorSaveContext escontext = {T_ErrorSaveContext};
+   int32       val;
 
-   errno = 0;
-   val = strtoint(base == 10 ? token : token + 2, &endptr, base);
-   if (*endptr != '\0' || errno == ERANGE)
+   val = pg_strtoint32_safe(token, (Node *) &escontext);
+   if (escontext.error_occurred)
    {
        /* integer too large (or contains decimal pt), treat it as a float */
        lval->str = pstrdup(token);
index 6bf6db6e27bbb0ca8fe904f41b37540c34aa6bf6..a83feea39674107ee1d95824696d8779eaec2e1b 100644 (file)
@@ -6968,10 +6968,7 @@ set_var_from_str(const char *str, const char *cp,
    }
 
    if (!isdigit((unsigned char) *cp))
-       ereturn(escontext, false,
-               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                errmsg("invalid input syntax for type %s: \"%s\"",
-                       "numeric", str)));
+       goto invalid_syntax;
 
    decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2);
 
@@ -6992,12 +6989,19 @@ set_var_from_str(const char *str, const char *cp,
        else if (*cp == '.')
        {
            if (have_dp)
-               ereturn(escontext, false,
-                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                        errmsg("invalid input syntax for type %s: \"%s\"",
-                               "numeric", str)));
+               goto invalid_syntax;
            have_dp = true;
            cp++;
+           /* decimal point must not be followed by underscore */
+           if (*cp == '_')
+               goto invalid_syntax;
+       }
+       else if (*cp == '_')
+       {
+           /* underscore must be followed by more digits */
+           cp++;
+           if (!isdigit((unsigned char) *cp))
+               goto invalid_syntax;
        }
        else
            break;
@@ -7010,17 +7014,8 @@ set_var_from_str(const char *str, const char *cp,
    /* Handle exponent, if any */
    if (*cp == 'e' || *cp == 'E')
    {
-       long        exponent;
-       char       *endptr;
-
-       cp++;
-       exponent = strtol(cp, &endptr, 10);
-       if (endptr == cp)
-           ereturn(escontext, false,
-                   (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                    errmsg("invalid input syntax for type %s: \"%s\"",
-                           "numeric", str)));
-       cp = endptr;
+       int64       exponent = 0;
+       bool        neg = false;
 
        /*
         * At this point, dweight and dscale can't be more than about
@@ -7030,10 +7025,43 @@ set_var_from_str(const char *str, const char *cp,
         * fit in storage format, make_result() will complain about it later;
         * for consistency use the same ereport errcode/text as make_result().
         */
-       if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2))
-           ereturn(escontext, false,
-                   (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-                    errmsg("value overflows numeric format")));
+
+       /* exponent sign */
+       cp++;
+       if (*cp == '+')
+           cp++;
+       else if (*cp == '-')
+       {
+           neg = true;
+           cp++;
+       }
+
+       /* exponent digits */
+       if (!isdigit((unsigned char) *cp))
+           goto invalid_syntax;
+
+       while (*cp)
+       {
+           if (isdigit((unsigned char) *cp))
+           {
+               exponent = exponent * 10 + (*cp++ - '0');
+               if (exponent > PG_INT32_MAX / 2)
+                   goto out_of_range;
+           }
+           else if (*cp == '_')
+           {
+               /* underscore must be followed by more digits */
+               cp++;
+               if (!isdigit((unsigned char) *cp))
+                   goto invalid_syntax;
+           }
+           else
+               break;
+       }
+
+       if (neg)
+           exponent = -exponent;
+
        dweight += (int) exponent;
        dscale -= (int) exponent;
        if (dscale < 0)
@@ -7085,6 +7113,17 @@ set_var_from_str(const char *str, const char *cp,
    *endptr = cp;
 
    return true;
+
+out_of_range:
+   ereturn(escontext, false,
+           (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+            errmsg("value overflows numeric format")));
+
+invalid_syntax:
+   ereturn(escontext, false,
+           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+            errmsg("invalid input syntax for type %s: \"%s\"",
+                   "numeric", str)));
 }
 
 
@@ -7167,6 +7206,13 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
                tmp = tmp * 16 + xdigit_value(*cp++);
                mul = mul * 16;
            }
+           else if (*cp == '_')
+           {
+               /* Underscore must be followed by more digits */
+               cp++;
+               if (!isxdigit((unsigned char) *cp))
+                   goto invalid_syntax;
+           }
            else
                break;
        }
@@ -7197,6 +7243,13 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
                tmp = tmp * 8 + (*cp++ - '0');
                mul = mul * 8;
            }
+           else if (*cp == '_')
+           {
+               /* Underscore must be followed by more digits */
+               cp++;
+               if (*cp < '0' || *cp > '7')
+                   goto invalid_syntax;
+           }
            else
                break;
        }
@@ -7227,6 +7280,13 @@ set_var_from_non_decimal_integer_str(const char *str, const char *cp, int sign,
                tmp = tmp * 2 + (*cp++ - '0');
                mul = mul * 2;
            }
+           else if (*cp == '_')
+           {
+               /* Underscore must be followed by more digits */
+               cp++;
+               if (*cp < '0' || *cp > '1')
+                   goto invalid_syntax;
+           }
            else
                break;
        }
index b0e412e7c67fc2130a744b76b0c49654ceccccbf..471fbb7ee63e6344ffc83db7aaa5b8023c4d2a90 100644 (file)
@@ -141,48 +141,99 @@ pg_strtoint16_safe(const char *s, Node *escontext)
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && isxdigit((unsigned char) *ptr))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT16_MIN / 16)))
-               goto out_of_range;
-
-           tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+           if (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 == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || !isxdigit((unsigned char) *ptr))
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT16_MIN / 8)))
-               goto out_of_range;
-
-           tmp = tmp * 8 + (*ptr++ - '0');
+           if (*ptr >= '0' && *ptr <= '7')
+           {
+               if (unlikely(tmp > -(PG_INT16_MIN / 8)))
+                   goto out_of_range;
+
+               tmp = tmp * 8 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || *ptr < '0' || *ptr > '7')
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT16_MIN / 2)))
-               goto out_of_range;
-
-           tmp = tmp * 2 + (*ptr++ - '0');
+           if (*ptr >= '0' && *ptr <= '1')
+           {
+               if (unlikely(tmp > -(PG_INT16_MIN / 2)))
+                   goto out_of_range;
+
+               tmp = tmp * 2 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || *ptr < '0' || *ptr > '1')
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else
    {
        firstdigit = ptr;
 
-       while (*ptr && isdigit((unsigned char) *ptr))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT16_MIN / 10)))
-               goto out_of_range;
-
-           tmp = tmp * 10 + (*ptr++ - '0');
+           if (isdigit((unsigned char) *ptr))
+           {
+               if (unlikely(tmp > -(PG_INT16_MIN / 10)))
+                   goto out_of_range;
+
+               tmp = tmp * 10 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore may not be first */
+               if (unlikely(ptr == firstdigit))
+                   goto invalid_syntax;
+               /* and it must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || !isdigit((unsigned char) *ptr))
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
 
@@ -268,48 +319,99 @@ pg_strtoint32_safe(const char *s, Node *escontext)
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && isxdigit((unsigned char) *ptr))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT32_MIN / 16)))
-               goto out_of_range;
-
-           tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+           if (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 == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || !isxdigit((unsigned char) *ptr))
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT32_MIN / 8)))
-               goto out_of_range;
-
-           tmp = tmp * 8 + (*ptr++ - '0');
+           if (*ptr >= '0' && *ptr <= '7')
+           {
+               if (unlikely(tmp > -(PG_INT32_MIN / 8)))
+                   goto out_of_range;
+
+               tmp = tmp * 8 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || *ptr < '0' || *ptr > '7')
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT32_MIN / 2)))
-               goto out_of_range;
-
-           tmp = tmp * 2 + (*ptr++ - '0');
+           if (*ptr >= '0' && *ptr <= '1')
+           {
+               if (unlikely(tmp > -(PG_INT32_MIN / 2)))
+                   goto out_of_range;
+
+               tmp = tmp * 2 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || *ptr < '0' || *ptr > '1')
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else
    {
        firstdigit = ptr;
 
-       while (*ptr && isdigit((unsigned char) *ptr))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT32_MIN / 10)))
-               goto out_of_range;
-
-           tmp = tmp * 10 + (*ptr++ - '0');
+           if (isdigit((unsigned char) *ptr))
+           {
+               if (unlikely(tmp > -(PG_INT32_MIN / 10)))
+                   goto out_of_range;
+
+               tmp = tmp * 10 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore may not be first */
+               if (unlikely(ptr == firstdigit))
+                   goto invalid_syntax;
+               /* and it must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || !isdigit((unsigned char) *ptr))
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
 
@@ -395,48 +497,99 @@ pg_strtoint64_safe(const char *s, Node *escontext)
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && isxdigit((unsigned char) *ptr))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT64_MIN / 16)))
-               goto out_of_range;
-
-           tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+           if (isxdigit((unsigned char) *ptr))
+           {
+               if (unlikely(tmp > -(PG_INT64_MIN / 16)))
+                   goto out_of_range;
+
+               tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || !isxdigit((unsigned char) *ptr))
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else if (ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O'))
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && (*ptr >= '0' && *ptr <= '7'))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT64_MIN / 8)))
-               goto out_of_range;
-
-           tmp = tmp * 8 + (*ptr++ - '0');
+           if (*ptr >= '0' && *ptr <= '7')
+           {
+               if (unlikely(tmp > -(PG_INT64_MIN / 8)))
+                   goto out_of_range;
+
+               tmp = tmp * 8 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || *ptr < '0' || *ptr > '7')
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else if (ptr[0] == '0' && (ptr[1] == 'b' || ptr[1] == 'B'))
    {
        firstdigit = ptr += 2;
 
-       while (*ptr && (*ptr >= '0' && *ptr <= '1'))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT64_MIN / 2)))
-               goto out_of_range;
-
-           tmp = tmp * 2 + (*ptr++ - '0');
+           if (*ptr >= '0' && *ptr <= '1')
+           {
+               if (unlikely(tmp > -(PG_INT64_MIN / 2)))
+                   goto out_of_range;
+
+               tmp = tmp * 2 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || *ptr < '0' || *ptr > '1')
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
    else
    {
        firstdigit = ptr;
 
-       while (*ptr && isdigit((unsigned char) *ptr))
+       while (*ptr)
        {
-           if (unlikely(tmp > -(PG_INT64_MIN / 10)))
-               goto out_of_range;
-
-           tmp = tmp * 10 + (*ptr++ - '0');
+           if (isdigit((unsigned char) *ptr))
+           {
+               if (unlikely(tmp > -(PG_INT64_MIN / 10)))
+                   goto out_of_range;
+
+               tmp = tmp * 10 + (*ptr++ - '0');
+           }
+           else if (*ptr == '_')
+           {
+               /* underscore may not be first */
+               if (unlikely(ptr == firstdigit))
+                   goto invalid_syntax;
+               /* and it must be followed by more digits */
+               ptr++;
+               if (*ptr == '\0' || !isdigit((unsigned char) *ptr))
+                   goto invalid_syntax;
+           }
+           else
+               break;
        }
    }
 
index bec50957391c85511d448b8a977ac3fbc4c735db..84754aca4a99b012b916ac3613a9b51e2fd1eba5 100644 (file)
@@ -333,19 +333,19 @@ 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}+
+decinteger     {decdigit}(_?{decdigit})*
+hexinteger     0[xX](_?{hexdigit})+
+octinteger     0[oO](_?{octdigit})+
+bininteger     0[bB](_?{bindigit})+
 
-hexfail            0[xX]
-octfail            0[oO]
-binfail            0[bB]
+hexfail            0[xX]_?
+octfail            0[oO]_?
+binfail            0[bB]_?
 
 numeric            (({decinteger}\.{decinteger}?)|(\.{decinteger}))
 numericfail        {decdigit}+\.\.
 
-real           ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
+real           ({decinteger}|{numeric})[Ee][-+]?{decinteger}
 realfail       ({decinteger}|{numeric})[Ee][-+]
 
 decinteger_junk    {decinteger}{ident_start}
index 75815bddb74b0f19e2719718f88580e11f3f378d..dcd567e8c3a1d6b5acbc0f3e98c31860de0a99d2 100644 (file)
@@ -361,19 +361,19 @@ 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}+
+decinteger     {decdigit}(_?{decdigit})*
+hexinteger     0[xX](_?{hexdigit})+
+octinteger     0[oO](_?{octdigit})+
+bininteger     0[bB](_?{bindigit})+
 
-hexfail            0[xX]
-octfail            0[oO]
-binfail            0[bB]
+hexfail            0[xX]_?
+octfail            0[oO]_?
+binfail            0[bB]_?
 
 numeric            (({decinteger}\.{decinteger}?)|(\.{decinteger}))
 numericfail        {decdigit}+\.\.
 
-real           ({decinteger}|{numeric})[Ee][-+]?{decdigit}+
+real           ({decinteger}|{numeric})[Ee][-+]?{decinteger}
 realfail       ({decinteger}|{numeric})[Ee][-+]
 
 decinteger_junk    {decinteger}{ident_start}
index 90cf6c289560beca4022b47247d43c60fe19e6ce..62d1679c28f038eacaae1dc02b3b3116f58528e4 100644 (file)
@@ -141,7 +141,7 @@ begin
   declare x int;
   begin
     -- we assume this will take longer than 1 second:
-    select count(*) into x from generate_series(1, 1000000000000);
+    select count(*) into x from generate_series(1, 1_000_000_000_000);
   exception
     when others then
       raise notice 'caught others?';
index c6c1ad894b5be4708ffa191f9f6c0c284bfb8547..5459b347e7f1f57a1a295121e88ad2d29aea2f8e 100644 (file)
@@ -88,7 +88,7 @@ begin
   declare x int;
   begin
     -- we assume this will take longer than 1 second:
-    select count(*) into x from generate_series(1, 1000000000000);
+    select count(*) into x from generate_series(1, 1_000_000_000_000);
   exception
     when others then
       raise notice 'caught others?';
index 08c333b75a2196fc89bfb6dc4a8bde977332ec7d..73b4ee023c54bf1388282e3c5a682877e9b05969 100644 (file)
@@ -440,3 +440,47 @@ SELECT int2 '-0x8001';
 ERROR:  value "-0x8001" is out of range for type smallint
 LINE 1: SELECT int2 '-0x8001';
                     ^
+-- underscores
+SELECT int2 '1_000';
+ int2 
+------
+ 1000
+(1 row)
+
+SELECT int2 '1_2_3';
+ int2 
+------
+  123
+(1 row)
+
+SELECT int2 '0xE_FF';
+ int2 
+------
+ 3839
+(1 row)
+
+SELECT int2 '0o2_73';
+ int2 
+------
+  187
+(1 row)
+
+SELECT int2 '0b_10_0101';
+ int2 
+------
+   37
+(1 row)
+
+-- error cases
+SELECT int2 '_100';
+ERROR:  invalid input syntax for type smallint: "_100"
+LINE 1: SELECT int2 '_100';
+                    ^
+SELECT int2 '100_';
+ERROR:  invalid input syntax for type smallint: "100_"
+LINE 1: SELECT int2 '100_';
+                    ^
+SELECT int2 '10__000';
+ERROR:  invalid input syntax for type smallint: "10__000"
+LINE 1: SELECT int2 '10__000';
+                    ^
index 8386c7cdff19358e729ebd42fc0fa33566831a64..9c20574ca5eb7aa6a8a7f45c96b37b99dee6ce11 100644 (file)
@@ -548,3 +548,47 @@ SELECT int4 '-0x80000001';
 ERROR:  value "-0x80000001" is out of range for type integer
 LINE 1: SELECT int4 '-0x80000001';
                     ^
+-- underscores
+SELECT int4 '1_000_000';
+  int4   
+---------
+ 1000000
+(1 row)
+
+SELECT int4 '1_2_3';
+ int4 
+------
+  123
+(1 row)
+
+SELECT int4 '0x1EEE_FFFF';
+   int4    
+-----------
+ 518979583
+(1 row)
+
+SELECT int4 '0o2_73';
+ int4 
+------
+  187
+(1 row)
+
+SELECT int4 '0b_10_0101';
+ int4 
+------
+   37
+(1 row)
+
+-- error cases
+SELECT int4 '_100';
+ERROR:  invalid input syntax for type integer: "_100"
+LINE 1: SELECT int4 '_100';
+                    ^
+SELECT int4 '100_';
+ERROR:  invalid input syntax for type integer: "100_"
+LINE 1: SELECT int4 '100_';
+                    ^
+SELECT int4 '100__000';
+ERROR:  invalid input syntax for type integer: "100__000"
+LINE 1: SELECT int4 '100__000';
+                    ^
index 5b62b51be9c91e8231873ce094dbdbce57cd2a23..d9dca64e8861e7e7cd1fef8955e5b40c364a7643 100644 (file)
@@ -1044,3 +1044,47 @@ SELECT int8 '-0x8000000000000001';
 ERROR:  value "-0x8000000000000001" is out of range for type bigint
 LINE 1: SELECT int8 '-0x8000000000000001';
                     ^
+-- underscores
+SELECT int8 '1_000_000';
+  int8   
+---------
+ 1000000
+(1 row)
+
+SELECT int8 '1_2_3';
+ int8 
+------
+  123
+(1 row)
+
+SELECT int8 '0x1EEE_FFFF';
+   int8    
+-----------
+ 518979583
+(1 row)
+
+SELECT int8 '0o2_73';
+ int8 
+------
+  187
+(1 row)
+
+SELECT int8 '0b_10_0101';
+ int8 
+------
+   37
+(1 row)
+
+-- error cases
+SELECT int8 '_100';
+ERROR:  invalid input syntax for type bigint: "_100"
+LINE 1: SELECT int8 '_100';
+                    ^
+SELECT int8 '100_';
+ERROR:  invalid input syntax for type bigint: "100_"
+LINE 1: SELECT int8 '100_';
+                    ^
+SELECT int8 '100__000';
+ERROR:  invalid input syntax for type bigint: "100__000"
+LINE 1: SELECT int8 '100__000';
+                    ^
index 94796522d99bbe3c0acc2d3845dc4bb35ac04495..56a3f3630a522170141d1e2d457649c47c21bc27 100644 (file)
@@ -2144,12 +2144,17 @@ INSERT INTO num_input_test(n1) VALUES (' -inf ');
 INSERT INTO num_input_test(n1) VALUES (' Infinity ');
 INSERT INTO num_input_test(n1) VALUES (' +inFinity ');
 INSERT INTO num_input_test(n1) VALUES (' -INFINITY ');
+INSERT INTO num_input_test(n1) VALUES ('12_000_000_000');
+INSERT INTO num_input_test(n1) VALUES ('12_000.123_456');
+INSERT INTO num_input_test(n1) VALUES ('23_000_000_000e-1_0');
+INSERT INTO num_input_test(n1) VALUES ('.000_000_000_123e1_0');
+INSERT INTO num_input_test(n1) VALUES ('.000_000_000_123e+1_1');
 INSERT INTO num_input_test(n1) VALUES ('0b10001110111100111100001001010');
-INSERT INTO num_input_test(n1) VALUES ('  -0B1010101101010100101010011000110011101011000111110000101011010010  ');
+INSERT INTO num_input_test(n1) VALUES ('  -0B_1010_1011_0101_0100_1010_1001_1000_1100_1110_1011_0001_1111_0000_1010_1101_0010  ');
 INSERT INTO num_input_test(n1) VALUES ('  +0o112402761777 ');
-INSERT INTO num_input_test(n1) VALUES ('-0O001255245230633431670261');
+INSERT INTO num_input_test(n1) VALUES ('-0O0012_5524_5230_6334_3167_0261');
 INSERT INTO num_input_test(n1) VALUES ('-0x0000000000000000000000000deadbeef');
-INSERT INTO num_input_test(n1) VALUES (' 0X30b1F33a6DF0bD4E64DF9BdA7D15 ');
+INSERT INTO num_input_test(n1) VALUES (' 0X_30b1_F33a_6DF0_bD4E_64DF_9BdA_7D15 ');
 -- bad inputs
 INSERT INTO num_input_test(n1) VALUES ('     ');
 ERROR:  invalid input syntax for type numeric: "     "
@@ -2195,6 +2200,38 @@ INSERT INTO num_input_test(n1) VALUES ('+ infinity');
 ERROR:  invalid input syntax for type numeric: "+ infinity"
 LINE 1: INSERT INTO num_input_test(n1) VALUES ('+ infinity');
                                                ^
+INSERT INTO num_input_test(n1) VALUES ('_123');
+ERROR:  invalid input syntax for type numeric: "_123"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('_123');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('123_');
+ERROR:  invalid input syntax for type numeric: "123_"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('123_');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('12__34');
+ERROR:  invalid input syntax for type numeric: "12__34"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('12__34');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('123_.456');
+ERROR:  invalid input syntax for type numeric: "123_.456"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('123_.456');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('123._456');
+ERROR:  invalid input syntax for type numeric: "123._456"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('123._456');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('1.2e_34');
+ERROR:  invalid input syntax for type numeric: "1.2e_34"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('1.2e_34');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('1.2e34_');
+ERROR:  invalid input syntax for type numeric: "1.2e34_"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('1.2e34_');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('1.2e3__4');
+ERROR:  invalid input syntax for type numeric: "1.2e3__4"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('1.2e3__4');
+                                               ^
 INSERT INTO num_input_test(n1) VALUES ('0b1112');
 ERROR:  invalid input syntax for type numeric: "0b1112"
 LINE 1: INSERT INTO num_input_test(n1) VALUES ('0b1112');
@@ -2215,6 +2252,18 @@ INSERT INTO num_input_test(n1) VALUES ('0x12.34');
 ERROR:  invalid input syntax for type numeric: "0x12.34"
 LINE 1: INSERT INTO num_input_test(n1) VALUES ('0x12.34');
                                                ^
+INSERT INTO num_input_test(n1) VALUES ('0x__1234');
+ERROR:  invalid input syntax for type numeric: "0x__1234"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('0x__1234');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('0x1234_');
+ERROR:  invalid input syntax for type numeric: "0x1234_"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('0x1234_');
+                                               ^
+INSERT INTO num_input_test(n1) VALUES ('0x12__34');
+ERROR:  invalid input syntax for type numeric: "0x12__34"
+LINE 1: INSERT INTO num_input_test(n1) VALUES ('0x12__34');
+                                               ^
 SELECT * FROM num_input_test;
                 n1                 
 -----------------------------------
@@ -2231,13 +2280,18 @@ SELECT * FROM num_input_test;
                           Infinity
                           Infinity
                          -Infinity
+                       12000000000
+                      12000.123456
+                      2.3000000000
+                              1.23
+                              12.3
                          299792458
              -12345678901234567890
                         9999999999
              -12345678900987654321
                        -3735928559
  987654321234567898765432123456789
-(19 rows)
+(24 rows)
 
 -- Also try it with non-error-throwing API
 SELECT pg_input_is_valid('34.5', 'numeric');
index deb26d31c3356132cf79fe4c12bf41fda577ea94..f662a5050ac004d344d9e10d0e45be6a164af105 100644 (file)
@@ -178,10 +178,6 @@ SELECT 0x0o;
 ERROR:  trailing junk after numeric literal at or near "0x0o"
 LINE 1: SELECT 0x0o;
                ^
-SELECT 1_2_3;
-ERROR:  trailing junk after numeric literal at or near "1_"
-LINE 1: SELECT 1_2_3;
-               ^
 SELECT 0.a;
 ERROR:  trailing junk after numeric literal at or near "0.a"
 LINE 1: SELECT 0.a;
@@ -246,6 +242,94 @@ SELECT 0x0y;
 ERROR:  trailing junk after numeric literal at or near "0x0y"
 LINE 1: SELECT 0x0y;
                ^
+-- underscores
+SELECT 1_000_000;
+ ?column? 
+----------
+  1000000
+(1 row)
+
+SELECT 1_2_3;
+ ?column? 
+----------
+      123
+(1 row)
+
+SELECT 0x1EEE_FFFF;
+ ?column?  
+-----------
+ 518979583
+(1 row)
+
+SELECT 0o2_73;
+ ?column? 
+----------
+      187
+(1 row)
+
+SELECT 0b_10_0101;
+ ?column? 
+----------
+       37
+(1 row)
+
+SELECT 1_000.000_005;
+  ?column?   
+-------------
+ 1000.000005
+(1 row)
+
+SELECT 1_000.;
+ ?column? 
+----------
+     1000
+(1 row)
+
+SELECT .000_005;
+ ?column? 
+----------
+ 0.000005
+(1 row)
+
+SELECT 1_000.5e0_1;
+ ?column? 
+----------
+    10005
+(1 row)
+
+-- error cases
+SELECT _100;
+ERROR:  column "_100" does not exist
+LINE 1: SELECT _100;
+               ^
+SELECT 100_;
+ERROR:  trailing junk after numeric literal at or near "100_"
+LINE 1: SELECT 100_;
+               ^
+SELECT 100__000;
+ERROR:  trailing junk after numeric literal at or near "100_"
+LINE 1: SELECT 100__000;
+               ^
+SELECT _1_000.5;
+ERROR:  syntax error at or near ".5"
+LINE 1: SELECT _1_000.5;
+                     ^
+SELECT 1_000_.5;
+ERROR:  trailing junk after numeric literal at or near "1_000_"
+LINE 1: SELECT 1_000_.5;
+               ^
+SELECT 1_000._5;
+ERROR:  trailing junk after numeric literal at or near "1_000._"
+LINE 1: SELECT 1_000._5;
+               ^
+SELECT 1_000.5_;
+ERROR:  trailing junk after numeric literal at or near "1_000.5_"
+LINE 1: SELECT 1_000.5_;
+               ^
+SELECT 1_000.5e_1;
+ERROR:  trailing junk after numeric literal at or near "1_000.5e"
+LINE 1: SELECT 1_000.5e_1;
+               ^
 --
 -- Test implicit type conversions
 -- This fails for Postgres v6.1 (and earlier?)
index 7555764c7794816a3680e3a99d75ec3e7f3d9ba6..d700c0062976610e0482aeba931fc6d32fb219a9 100644 (file)
@@ -1503,7 +1503,7 @@ explain (costs off) select * from like_op_noprune where a like '%BC';
 create table lparted_by_int2 (a smallint) partition by list (a);
 create table lparted_by_int2_1 partition of lparted_by_int2 for values in (1);
 create table lparted_by_int2_16384 partition of lparted_by_int2 for values in (16384);
-explain (costs off) select * from lparted_by_int2 where a = 100000000000000;
+explain (costs off) select * from lparted_by_int2 where a = 100_000_000_000_000;
         QUERY PLAN        
 --------------------------
  Result
@@ -1514,7 +1514,7 @@ create table rparted_by_int2 (a smallint) partition by range (a);
 create table rparted_by_int2_1 partition of rparted_by_int2 for values from (1) to (10);
 create table rparted_by_int2_16384 partition of rparted_by_int2 for values from (10) to (16384);
 -- all partitions pruned
-explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
+explain (costs off) select * from rparted_by_int2 where a > 100_000_000_000_000;
         QUERY PLAN        
 --------------------------
  Result
@@ -1523,7 +1523,7 @@ explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
 
 create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values from (16384) to (maxvalue);
 -- all partitions but rparted_by_int2_maxvalue pruned
-explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
+explain (costs off) select * from rparted_by_int2 where a > 100_000_000_000_000;
                       QUERY PLAN                      
 ------------------------------------------------------
  Seq Scan on rparted_by_int2_maxvalue rparted_by_int2
index a812235ee50fd54a11d6089f8583ea2f7811f966..ce8ac9796323ecaa8f63247108c6e83ce8a654b8 100644 (file)
@@ -141,3 +141,17 @@ SELECT int2 '-0o100000';
 SELECT int2 '-0o100001';
 SELECT int2 '-0x8000';
 SELECT int2 '-0x8001';
+
+
+-- underscores
+
+SELECT int2 '1_000';
+SELECT int2 '1_2_3';
+SELECT int2 '0xE_FF';
+SELECT int2 '0o2_73';
+SELECT int2 '0b_10_0101';
+
+-- error cases
+SELECT int2 '_100';
+SELECT int2 '100_';
+SELECT int2 '10__000';
index 9e6a40408ab87f0aef4382af0c7657da9726eab7..146963edfbd3696289b9ffdb385d4432a7391f52 100644 (file)
@@ -196,3 +196,17 @@ SELECT int4 '-0o20000000000';
 SELECT int4 '-0o20000000001';
 SELECT int4 '-0x80000000';
 SELECT int4 '-0x80000001';
+
+
+-- underscores
+
+SELECT int4 '1_000_000';
+SELECT int4 '1_2_3';
+SELECT int4 '0x1EEE_FFFF';
+SELECT int4 '0o2_73';
+SELECT int4 '0b_10_0101';
+
+-- error cases
+SELECT int4 '_100';
+SELECT int4 '100_';
+SELECT int4 '100__000';
index 06f273ed5845cedb481876e9f78befb0bdfb304e..c85717c072ae5fef66c91d82c3dc3eb7c0eea03c 100644 (file)
@@ -277,3 +277,17 @@ SELECT int8 '-0o1000000000000000000000';
 SELECT int8 '-0o1000000000000000000001';
 SELECT int8 '-0x8000000000000000';
 SELECT int8 '-0x8000000000000001';
+
+
+-- underscores
+
+SELECT int8 '1_000_000';
+SELECT int8 '1_2_3';
+SELECT int8 '0x1EEE_FFFF';
+SELECT int8 '0o2_73';
+SELECT int8 '0b_10_0101';
+
+-- error cases
+SELECT int8 '_100';
+SELECT int8 '100_';
+SELECT int8 '100__000';
index fe93714dd13d8a98621a9778cb0ee0bbe20e9d42..2db7656e84c06cfbc5ab07670069f51d7d0914a3 100644 (file)
@@ -1039,12 +1039,17 @@ INSERT INTO num_input_test(n1) VALUES (' -inf ');
 INSERT INTO num_input_test(n1) VALUES (' Infinity ');
 INSERT INTO num_input_test(n1) VALUES (' +inFinity ');
 INSERT INTO num_input_test(n1) VALUES (' -INFINITY ');
+INSERT INTO num_input_test(n1) VALUES ('12_000_000_000');
+INSERT INTO num_input_test(n1) VALUES ('12_000.123_456');
+INSERT INTO num_input_test(n1) VALUES ('23_000_000_000e-1_0');
+INSERT INTO num_input_test(n1) VALUES ('.000_000_000_123e1_0');
+INSERT INTO num_input_test(n1) VALUES ('.000_000_000_123e+1_1');
 INSERT INTO num_input_test(n1) VALUES ('0b10001110111100111100001001010');
-INSERT INTO num_input_test(n1) VALUES ('  -0B1010101101010100101010011000110011101011000111110000101011010010  ');
+INSERT INTO num_input_test(n1) VALUES ('  -0B_1010_1011_0101_0100_1010_1001_1000_1100_1110_1011_0001_1111_0000_1010_1101_0010  ');
 INSERT INTO num_input_test(n1) VALUES ('  +0o112402761777 ');
-INSERT INTO num_input_test(n1) VALUES ('-0O001255245230633431670261');
+INSERT INTO num_input_test(n1) VALUES ('-0O0012_5524_5230_6334_3167_0261');
 INSERT INTO num_input_test(n1) VALUES ('-0x0000000000000000000000000deadbeef');
-INSERT INTO num_input_test(n1) VALUES (' 0X30b1F33a6DF0bD4E64DF9BdA7D15 ');
+INSERT INTO num_input_test(n1) VALUES (' 0X_30b1_F33a_6DF0_bD4E_64DF_9BdA_7D15 ');
 
 -- bad inputs
 INSERT INTO num_input_test(n1) VALUES ('     ');
@@ -1058,11 +1063,22 @@ INSERT INTO num_input_test(n1) VALUES (' N aN ');
 INSERT INTO num_input_test(n1) VALUES ('+NaN');
 INSERT INTO num_input_test(n1) VALUES ('-NaN');
 INSERT INTO num_input_test(n1) VALUES ('+ infinity');
+INSERT INTO num_input_test(n1) VALUES ('_123');
+INSERT INTO num_input_test(n1) VALUES ('123_');
+INSERT INTO num_input_test(n1) VALUES ('12__34');
+INSERT INTO num_input_test(n1) VALUES ('123_.456');
+INSERT INTO num_input_test(n1) VALUES ('123._456');
+INSERT INTO num_input_test(n1) VALUES ('1.2e_34');
+INSERT INTO num_input_test(n1) VALUES ('1.2e34_');
+INSERT INTO num_input_test(n1) VALUES ('1.2e3__4');
 INSERT INTO num_input_test(n1) VALUES ('0b1112');
 INSERT INTO num_input_test(n1) VALUES ('0c1112');
 INSERT INTO num_input_test(n1) VALUES ('0o12345678');
 INSERT INTO num_input_test(n1) VALUES ('0x1eg');
 INSERT INTO num_input_test(n1) VALUES ('0x12.34');
+INSERT INTO num_input_test(n1) VALUES ('0x__1234');
+INSERT INTO num_input_test(n1) VALUES ('0x1234_');
+INSERT INTO num_input_test(n1) VALUES ('0x12__34');
 
 SELECT * FROM num_input_test;
 
index 310d9e57663eb54366ef3b533477a52af7b52d31..1941c58e681f47117e937f70ef33f5df32afe536 100644 (file)
@@ -45,7 +45,6 @@ SELECT -0x8000000000000001;
 -- error cases
 SELECT 123abc;
 SELECT 0x0o;
-SELECT 1_2_3;
 SELECT 0.a;
 SELECT 0.0a;
 SELECT .0a;
@@ -66,6 +65,29 @@ SELECT 0x;
 SELECT 1x;
 SELECT 0x0y;
 
+-- underscores
+SELECT 1_000_000;
+SELECT 1_2_3;
+SELECT 0x1EEE_FFFF;
+SELECT 0o2_73;
+SELECT 0b_10_0101;
+
+SELECT 1_000.000_005;
+SELECT 1_000.;
+SELECT .000_005;
+SELECT 1_000.5e0_1;
+
+-- error cases
+SELECT _100;
+SELECT 100_;
+SELECT 100__000;
+
+SELECT _1_000.5;
+SELECT 1_000_.5;
+SELECT 1_000._5;
+SELECT 1_000.5_;
+SELECT 1_000.5e_1;
+
 
 --
 -- Test implicit type conversions
index d70bd8610cb02173a49fb193f2c82fba91f147f2..fb0583f924480d49426eb23d67013b71aa697785 100644 (file)
@@ -283,16 +283,16 @@ explain (costs off) select * from like_op_noprune where a like '%BC';
 create table lparted_by_int2 (a smallint) partition by list (a);
 create table lparted_by_int2_1 partition of lparted_by_int2 for values in (1);
 create table lparted_by_int2_16384 partition of lparted_by_int2 for values in (16384);
-explain (costs off) select * from lparted_by_int2 where a = 100000000000000;
+explain (costs off) select * from lparted_by_int2 where a = 100_000_000_000_000;
 
 create table rparted_by_int2 (a smallint) partition by range (a);
 create table rparted_by_int2_1 partition of rparted_by_int2 for values from (1) to (10);
 create table rparted_by_int2_16384 partition of rparted_by_int2 for values from (10) to (16384);
 -- all partitions pruned
-explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
+explain (costs off) select * from rparted_by_int2 where a > 100_000_000_000_000;
 create table rparted_by_int2_maxvalue partition of rparted_by_int2 for values from (16384) to (maxvalue);
 -- all partitions but rparted_by_int2_maxvalue pruned
-explain (costs off) select * from rparted_by_int2 where a > 100000000000000;
+explain (costs off) select * from rparted_by_int2 where a > 100_000_000_000_000;
 
 drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2;