Allow underscores in integer and numeric constants.
authorDean Rasheed <[email protected]>
Sat, 4 Feb 2023 09:48:51 +0000 (09:48 +0000)
committerDean Rasheed <[email protected]>
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 (<literal>e</literal>), 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.
     </para>
@@ -695,23 +696,24 @@ $function$
     </para>
 
     <para>
-     Additionally, non-decimal integer constants can be used in these forms:
+     Additionally, non-decimal integer constants are accepted 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
+     where <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-7), and <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
+     These are some examples of valid non-decimal integer constants:
+<literallayout>
+0b100101
 0B10011001
 0o273
 0O755
@@ -720,13 +722,21 @@ $function$
 </literallayout>
     </para>
 
-    <note>
-     <para>
-      Non-decimal integer constants are currently only supported in the range
-      of the <type>bigint</type> type (see <xref
-      linkend="datatype-numeric-table"/>).
-     </para>
-    </note>
+    <para>
+     For visual grouping, underscores can be inserted between digits.  These
+     have no further effect on the value of the constant.  For example:
+<literallayout>
+1_500_000_000
+0b10001000_00000000
+0o_1_755
+0xFFFF_FFFF
+1.618_034
+</literallayout>
+     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.
+    </para>
 
     <para>
      <indexterm><primary>integer</primary></indexterm>
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;