Allow underscores in integer and numeric constants.

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
This commit is contained in:
Dean Rasheed 2023-02-04 09:48:51 +00:00
parent 1b6f632a35
commit faff8f8e47
22 changed files with 712 additions and 172 deletions

View 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>

View 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

View 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

View File

@ -37,10 +37,12 @@
#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);

View 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;
}

View 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;
if (isxdigit((unsigned char) *ptr))
{
if (unlikely(tmp > -(PG_INT16_MIN / 16)))
goto out_of_range;
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
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;
if (*ptr >= '0' && *ptr <= '7')
{
if (unlikely(tmp > -(PG_INT16_MIN / 8)))
goto out_of_range;
tmp = tmp * 8 + (*ptr++ - '0');
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;
if (*ptr >= '0' && *ptr <= '1')
{
if (unlikely(tmp > -(PG_INT16_MIN / 2)))
goto out_of_range;
tmp = tmp * 2 + (*ptr++ - '0');
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;
if (isdigit((unsigned char) *ptr))
{
if (unlikely(tmp > -(PG_INT16_MIN / 10)))
goto out_of_range;
tmp = tmp * 10 + (*ptr++ - '0');
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;
if (isxdigit((unsigned char) *ptr))
{
if (unlikely(tmp > -(PG_INT32_MIN / 16)))
goto out_of_range;
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
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;
if (*ptr >= '0' && *ptr <= '7')
{
if (unlikely(tmp > -(PG_INT32_MIN / 8)))
goto out_of_range;
tmp = tmp * 8 + (*ptr++ - '0');
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;
if (*ptr >= '0' && *ptr <= '1')
{
if (unlikely(tmp > -(PG_INT32_MIN / 2)))
goto out_of_range;
tmp = tmp * 2 + (*ptr++ - '0');
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;
if (isdigit((unsigned char) *ptr))
{
if (unlikely(tmp > -(PG_INT32_MIN / 10)))
goto out_of_range;
tmp = tmp * 10 + (*ptr++ - '0');
tmp = tmp * 10 + (*ptr++ - '0');
}
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;
if (isxdigit((unsigned char) *ptr))
{
if (unlikely(tmp > -(PG_INT64_MIN / 16)))
goto out_of_range;
tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++];
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;
if (*ptr >= '0' && *ptr <= '7')
{
if (unlikely(tmp > -(PG_INT64_MIN / 8)))
goto out_of_range;
tmp = tmp * 8 + (*ptr++ - '0');
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;
if (*ptr >= '0' && *ptr <= '1')
{
if (unlikely(tmp > -(PG_INT64_MIN / 2)))
goto out_of_range;
tmp = tmp * 2 + (*ptr++ - '0');
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;
if (isdigit((unsigned char) *ptr))
{
if (unlikely(tmp > -(PG_INT64_MIN / 10)))
goto out_of_range;
tmp = tmp * 10 + (*ptr++ - '0');
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;
}
}

View 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}

View 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}

View 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?';

View 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?';

View 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';
^

View 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';
^

View 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';
^

View 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');

View 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?)

View 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

View 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';

View 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';

View 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';

View 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;

View 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

View 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;