From 36aef78423e9adc6ebe72fb2a3cf43e385a2caca Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Tue, 27 Dec 2022 10:10:18 +0100 Subject: [PATCH v1] Underscores in numeric literals Add support for underscores in numeric literals, for visual grouping, like 1_500_000_000 0b10001000_00000000 0o_1_755 0xFFFF_FFFF 1.618_034 per SQL:202x draft. This adds support in the lexer as well as in the integer type input functions. TODO: float/numeric type input support Discussion: https://www.postgresql.org/message-id/flat/b239564c-cad0-b23e-c57e-166d883cb97d@enterprisedb.com --- doc/src/sgml/syntax.sgml | 14 ++ src/backend/catalog/sql_features.txt | 1 + src/backend/parser/scan.l | 63 ++++++-- src/backend/utils/adt/numutils.c | 144 ++++++++++++++++-- src/fe_utils/psqlscan.l | 16 +- src/interfaces/ecpg/preproc/pgc.l | 16 +- src/pl/plpgsql/src/expected/plpgsql_trap.out | 2 +- src/pl/plpgsql/src/sql/plpgsql_trap.sql | 2 +- src/test/regress/expected/int2.out | 44 ++++++ src/test/regress/expected/int4.out | 44 ++++++ src/test/regress/expected/int8.out | 44 ++++++ src/test/regress/expected/numerology.out | 92 ++++++++++- src/test/regress/expected/partition_prune.out | 6 +- src/test/regress/sql/int2.sql | 14 ++ src/test/regress/sql/int4.sql | 14 ++ src/test/regress/sql/int8.sql | 14 ++ src/test/regress/sql/numerology.sql | 24 ++- src/test/regress/sql/partition_prune.sql | 6 +- 18 files changed, 509 insertions(+), 51 deletions(-) diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index 956182e7c6..27e53b4b46 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -728,6 +728,20 @@ Numeric Constants + + For visual grouping, underscores can be inserted between digits. These + have no further effect on the value of the literal. For example: +1_500_000_000 +0b10001000_00000000 +0o_1_755 +0xFFFF_FFFF +1.618_034 + + Underscores are not allowed at the start or end of a numeric constant or + a group of digits (that is, immediately before or after a period or the + e), and more than one underscore in a row is not allowed. + + integer bigint diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt index abad216b7e..3766762ae3 100644 --- a/src/backend/catalog/sql_features.txt +++ b/src/backend/catalog/sql_features.txt @@ -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 diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index 9ad9e0c8ba..a1ea94ef06 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -124,6 +124,7 @@ static void addlit(char *ytext, int yleng, core_yyscan_t yyscanner); static void addlitchar(unsigned char ychar, core_yyscan_t yyscanner); static char *litbufdup(core_yyscan_t yyscanner); static unsigned char unescape_single_char(unsigned char c, core_yyscan_t yyscanner); +static char *strip_underscores(const char *in); static int process_integer_literal(const char *token, YYSTYPE *lval, int base); static void addunicode(pg_wchar c, yyscan_t yyscanner); @@ -395,19 +396,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} @@ -1028,7 +1029,7 @@ other . } {numeric} { SET_YYLLOC(); - yylval->str = pstrdup(yytext); + yylval->str = strip_underscores(yytext); return FCONST; } {numericfail} { @@ -1039,7 +1040,7 @@ other . } {real} { SET_YYLLOC(); - yylval->str = pstrdup(yytext); + yylval->str = strip_underscores(yytext); return FCONST; } {realfail} { @@ -1357,6 +1358,30 @@ litbufdup(core_yyscan_t yyscanner) return new; } +static char * +strip_underscores(const char *in) +{ + if (strchr(in, '_')) + { + char *out = palloc(strlen(in)); + const char *p1; + char *p2; + + p1 = in; + p2 = out; + while (*p1) + { + if (*p1 != '_') + *p2++ = *p1; + p1++; + } + *p2 = '\0'; + return out; + } + else + return pstrdup(in); +} + /* * Process {decinteger}, {hexinteger}, etc. Note this will also do the right * thing with {numeric}, ie digits and a decimal point. @@ -1367,6 +1392,24 @@ process_integer_literal(const char *token, YYSTYPE *lval, int base) int val; char *endptr; + if (strchr(token, '_')) + { + char *newtoken = palloc(strlen(token)); + const char *p1; + char *p2; + + p1 = token; + p2 = newtoken; + while (*p1) + { + if (*p1 != '_') + *p2++ = *p1; + p1++; + } + *p2 = '\0'; + token = newtoken; + } + errno = 0; val = strtoint(base == 10 ? token : token + 2, &endptr, base); if (*endptr != '\0' || errno == ERANGE) diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index 7cded73e6e..07f04d07cb 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -141,8 +141,16 @@ pg_strtoint16_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && isxdigit((unsigned char) *ptr)) + while (*ptr && (isxdigit((unsigned char) *ptr) || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT16_MIN / 16))) goto out_of_range; @@ -153,8 +161,16 @@ pg_strtoint16_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && (*ptr >= '0' && *ptr <= '7')) + while (*ptr && ((*ptr >= '0' && *ptr <= '7') || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT16_MIN / 8))) goto out_of_range; @@ -165,8 +181,16 @@ pg_strtoint16_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && (*ptr >= '0' && *ptr <= '1')) + while (*ptr && ((*ptr >= '0' && *ptr <= '1') || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT16_MIN / 2))) goto out_of_range; @@ -177,8 +201,20 @@ pg_strtoint16_safe(const char *s, Node *escontext) { firstdigit = ptr; - while (*ptr && isdigit((unsigned char) *ptr)) + while (*ptr && (isdigit((unsigned char) *ptr) || *ptr == '_')) { + if (*ptr == '_') + { + /* underscore may not be first */ + if (unlikely(ptr == firstdigit)) + goto invalid_syntax; + /* not two in a row */ + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT16_MIN / 10))) goto out_of_range; @@ -190,6 +226,10 @@ pg_strtoint16_safe(const char *s, Node *escontext) if (unlikely(ptr == firstdigit)) goto invalid_syntax; + /* underscore may not be last */ + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + /* allow trailing whitespace, but not other trailing chars */ while (*ptr != '\0' && isspace((unsigned char) *ptr)) ptr++; @@ -268,8 +308,16 @@ pg_strtoint32_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && isxdigit((unsigned char) *ptr)) + while (*ptr && (isxdigit((unsigned char) *ptr) || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT32_MIN / 16))) goto out_of_range; @@ -280,8 +328,16 @@ pg_strtoint32_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && (*ptr >= '0' && *ptr <= '7')) + while (*ptr && ((*ptr >= '0' && *ptr <= '7') || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT32_MIN / 8))) goto out_of_range; @@ -292,8 +348,16 @@ pg_strtoint32_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && (*ptr >= '0' && *ptr <= '1')) + while (*ptr && ((*ptr >= '0' && *ptr <= '1') || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT32_MIN / 2))) goto out_of_range; @@ -304,8 +368,20 @@ pg_strtoint32_safe(const char *s, Node *escontext) { firstdigit = ptr; - while (*ptr && isdigit((unsigned char) *ptr)) + while (*ptr && (isdigit((unsigned char) *ptr) || *ptr == '_')) { + if (*ptr == '_') + { + /* underscore may not be first */ + if (unlikely(ptr == firstdigit)) + goto invalid_syntax; + /* not two in a row */ + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT32_MIN / 10))) goto out_of_range; @@ -317,6 +393,10 @@ pg_strtoint32_safe(const char *s, Node *escontext) if (unlikely(ptr == firstdigit)) goto invalid_syntax; + /* underscore may not be last */ + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + /* allow trailing whitespace, but not other trailing chars */ while (*ptr != '\0' && isspace((unsigned char) *ptr)) ptr++; @@ -395,8 +475,16 @@ pg_strtoint64_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && isxdigit((unsigned char) *ptr)) + while (*ptr && (isxdigit((unsigned char) *ptr) || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT64_MIN / 16))) goto out_of_range; @@ -407,8 +495,16 @@ pg_strtoint64_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && (*ptr >= '0' && *ptr <= '7')) + while (*ptr && ((*ptr >= '0' && *ptr <= '7') || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT64_MIN / 8))) goto out_of_range; @@ -419,8 +515,16 @@ pg_strtoint64_safe(const char *s, Node *escontext) { firstdigit = ptr += 2; - while (*ptr && (*ptr >= '0' && *ptr <= '1')) + while (*ptr && ((*ptr >= '0' && *ptr <= '1') || *ptr == '_')) { + if (*ptr == '_') + { + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT64_MIN / 2))) goto out_of_range; @@ -431,8 +535,20 @@ pg_strtoint64_safe(const char *s, Node *escontext) { firstdigit = ptr; - while (*ptr && isdigit((unsigned char) *ptr)) + while (*ptr && (isdigit((unsigned char) *ptr) || *ptr == '_')) { + if (*ptr == '_') + { + /* underscore may not be first */ + if (unlikely(ptr == firstdigit)) + goto invalid_syntax; + /* not two in a row */ + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + ptr++; + continue; + } + if (unlikely(tmp > -(PG_INT64_MIN / 10))) goto out_of_range; @@ -444,6 +560,10 @@ pg_strtoint64_safe(const char *s, Node *escontext) if (unlikely(ptr == firstdigit)) goto invalid_syntax; + /* underscore may not be last */ + if (unlikely(*(ptr - 1) == '_')) + goto invalid_syntax; + /* allow trailing whitespace, but not other trailing chars */ while (*ptr != '\0' && isspace((unsigned char) *ptr)) ptr++; diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l index cb1fc52138..44f80e4db4 100644 --- a/src/fe_utils/psqlscan.l +++ b/src/fe_utils/psqlscan.l @@ -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} diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index 2c09c6cb4f..2761ae34b8 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -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} diff --git a/src/pl/plpgsql/src/expected/plpgsql_trap.out b/src/pl/plpgsql/src/expected/plpgsql_trap.out index 90cf6c2895..62d1679c28 100644 --- a/src/pl/plpgsql/src/expected/plpgsql_trap.out +++ b/src/pl/plpgsql/src/expected/plpgsql_trap.out @@ -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?'; diff --git a/src/pl/plpgsql/src/sql/plpgsql_trap.sql b/src/pl/plpgsql/src/sql/plpgsql_trap.sql index c6c1ad894b..5459b347e7 100644 --- a/src/pl/plpgsql/src/sql/plpgsql_trap.sql +++ b/src/pl/plpgsql/src/sql/plpgsql_trap.sql @@ -88,7 +88,7 @@ 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?'; diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out index 08c333b75a..73b4ee023c 100644 --- a/src/test/regress/expected/int2.out +++ b/src/test/regress/expected/int2.out @@ -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'; + ^ diff --git a/src/test/regress/expected/int4.out b/src/test/regress/expected/int4.out index 8386c7cdff..9c20574ca5 100644 --- a/src/test/regress/expected/int4.out +++ b/src/test/regress/expected/int4.out @@ -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'; + ^ diff --git a/src/test/regress/expected/int8.out b/src/test/regress/expected/int8.out index 5b62b51be9..d9dca64e88 100644 --- a/src/test/regress/expected/int8.out +++ b/src/test/regress/expected/int8.out @@ -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'; + ^ diff --git a/src/test/regress/expected/numerology.out b/src/test/regress/expected/numerology.out index 15cd6b1672..6b9b089cb5 100644 --- a/src/test/regress/expected/numerology.out +++ b/src/test/regress/expected/numerology.out @@ -166,10 +166,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; @@ -234,6 +230,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?) diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 7555764c77..d700c00629 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -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 diff --git a/src/test/regress/sql/int2.sql b/src/test/regress/sql/int2.sql index a812235ee5..ce8ac97963 100644 --- a/src/test/regress/sql/int2.sql +++ b/src/test/regress/sql/int2.sql @@ -141,3 +141,17 @@ 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'; diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql index 9e6a40408a..146963edfb 100644 --- a/src/test/regress/sql/int4.sql +++ b/src/test/regress/sql/int4.sql @@ -196,3 +196,17 @@ 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'; diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql index 06f273ed58..c85717c072 100644 --- a/src/test/regress/sql/int8.sql +++ b/src/test/regress/sql/int8.sql @@ -277,3 +277,17 @@ 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'; diff --git a/src/test/regress/sql/numerology.sql b/src/test/regress/sql/numerology.sql index 310d9e5766..1941c58e68 100644 --- a/src/test/regress/sql/numerology.sql +++ b/src/test/regress/sql/numerology.sql @@ -45,7 +45,6 @@ -- error cases SELECT 123abc; SELECT 0x0o; -SELECT 1_2_3; SELECT 0.a; SELECT 0.0a; SELECT .0a; @@ -66,6 +65,29 @@ 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 diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index d70bd8610c..fb0583f924 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -283,16 +283,16 @@ CREATE TABLE part_rev (b INT, c INT, a INT); 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; base-commit: 63c844a0a5d70cdbd6ae0470d582d39e75ad8d66 -- 2.39.0