From faff8f8e47f18c7d589453e2e0d841d2bd96c1ac Mon Sep 17 00:00:00 2001 From: Dean Rasheed Date: Sat, 4 Feb 2023 09:48:51 +0000 Subject: [PATCH] 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 --- doc/src/sgml/syntax.sgml | 36 ++- src/backend/catalog/sql_features.txt | 1 + src/backend/parser/parse_node.c | 43 +-- src/backend/parser/scan.l | 27 +- src/backend/utils/adt/numeric.c | 106 +++++-- src/backend/utils/adt/numutils.c | 273 ++++++++++++++---- 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/numeric.out | 62 +++- 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/numeric.sql | 22 +- src/test/regress/sql/numerology.sql | 24 +- src/test/regress/sql/partition_prune.sql | 6 +- 22 files changed, 724 insertions(+), 184 deletions(-) diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index 0ccddea310d..5668ab01433 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -677,7 +677,8 @@ $function$ decimal point, if one is used. At least one digit must follow the exponent marker (e), if one is present. There cannot be any spaces or other characters embedded in the - constant. Note that any leading plus or minus sign is not actually + constant, except for underscores, which can be used for visual grouping as + described below. Note that any leading plus or minus sign is not actually considered part of the constant; it is an operator applied to the constant. @@ -695,23 +696,24 @@ $function$ - Additionally, non-decimal integer constants can be used in these forms: + Additionally, non-decimal integer constants are accepted in these forms: 0xhexdigits 0ooctdigits 0bbindigits - hexdigits is one or more hexadecimal digits + where hexdigits is one or more hexadecimal digits (0-9, A-F), octdigits is one or more octal - digits (0-7), bindigits is one or more binary + digits (0-7), and bindigits is one or more binary digits (0 or 1). Hexadecimal digits and the radix prefixes can be in upper or lower case. Note that only integers can have non-decimal forms, not numbers with fractional parts. - These are some examples of this: -0b100101 + These are some examples of valid non-decimal integer constants: + +0b100101 0B10011001 0o273 0O755 @@ -720,13 +722,21 @@ $function$ - - - Non-decimal integer constants are currently only supported in the range - of the bigint type (see ). - - + + For visual grouping, underscores can be inserted between digits. These + have no further effect on the value of the constant. For example: + +1_500_000_000 +0b10001000_00000000 +0o_1_755 +0xFFFF_FFFF +1.618_034 + + Underscores are not allowed at the start or end of a numeric constant or + a group of digits (that is, immediately before or after the decimal point + or the exponent marker), and more than one underscore in a row is not + allowed. + integer diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt index abad216b7ee..3766762ae36 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/parse_node.c b/src/backend/parser/parse_node.c index f1967a33bc0..5020b9f0810 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -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 diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index 1e821d4c9e2..b2216a9eacd 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -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); diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 6bf6db6e27b..a83feea3967 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -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; } diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index b0e412e7c67..471fbb7ee63 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -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; } } diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l index bec50957391..84754aca4a9 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 75815bddb74..dcd567e8c3a 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 90cf6c28956..62d1679c28f 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 c6c1ad894b5..5459b347e7f 100644 --- a/src/pl/plpgsql/src/sql/plpgsql_trap.sql +++ b/src/pl/plpgsql/src/sql/plpgsql_trap.sql @@ -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?'; diff --git a/src/test/regress/expected/int2.out b/src/test/regress/expected/int2.out index 08c333b75a2..73b4ee023c5 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 8386c7cdff1..9c20574ca5e 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 5b62b51be9c..d9dca64e886 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/numeric.out b/src/test/regress/expected/numeric.out index 94796522d99..56a3f3630a5 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -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'); diff --git a/src/test/regress/expected/numerology.out b/src/test/regress/expected/numerology.out index deb26d31c33..f662a5050ac 100644 --- a/src/test/regress/expected/numerology.out +++ b/src/test/regress/expected/numerology.out @@ -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?) diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 7555764c779..d700c006297 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 a812235ee50..ce8ac979632 100644 --- a/src/test/regress/sql/int2.sql +++ b/src/test/regress/sql/int2.sql @@ -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'; diff --git a/src/test/regress/sql/int4.sql b/src/test/regress/sql/int4.sql index 9e6a40408ab..146963edfbd 100644 --- a/src/test/regress/sql/int4.sql +++ b/src/test/regress/sql/int4.sql @@ -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'; diff --git a/src/test/regress/sql/int8.sql b/src/test/regress/sql/int8.sql index 06f273ed584..c85717c072a 100644 --- a/src/test/regress/sql/int8.sql +++ b/src/test/regress/sql/int8.sql @@ -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'; diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql index fe93714dd13..2db7656e84c 100644 --- a/src/test/regress/sql/numeric.sql +++ b/src/test/regress/sql/numeric.sql @@ -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; diff --git a/src/test/regress/sql/numerology.sql b/src/test/regress/sql/numerology.sql index 310d9e57663..1941c58e681 100644 --- a/src/test/regress/sql/numerology.sql +++ b/src/test/regress/sql/numerology.sql @@ -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 diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index d70bd8610cb..fb0583f9244 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -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; -- 2.30.2