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>
</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
</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>
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
#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"
{
/* 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
#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"
}
%{
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}
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);
}
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);
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;
/* 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
* 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)
*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)));
}
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;
}
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;
}
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;
}
{
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;
}
}
{
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;
}
}
{
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;
}
}
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}
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}
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?';
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?';
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';
+ ^
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';
+ ^
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';
+ ^
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: " "
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');
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
-----------------------------------
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');
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;
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?)
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
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
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
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';
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';
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';
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 (' ');
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;
-- error cases
SELECT 123abc;
SELECT 0x0o;
-SELECT 1_2_3;
SELECT 0.a;
SELECT 0.0a;
SELECT .0a;
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
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;