mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
SQL JSON path enhanced numeric literals
Add support for non-decimal integer literals and underscores in numeric literals to SQL JSON path language. This follows the rules of ECMAScript, as referred to by the SQL standard. Internally, all the numeric literal parsing of jsonpath goes through numeric_in, which already supports all this, so this patch is just a bit of lexer work and some tests and documentation. Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/b11b25bb-6ec1-d42f-cedd-311eae59e1fb@enterprisedb.com
This commit is contained in:
parent
6949b921d5
commit
102a5c164a
@ -779,6 +779,18 @@ UPDATE table_name SET jsonb_field[1]['a'] = '1';
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Numeric literals in SQL/JSON path expressions follow JavaScript rules,
|
||||
which are different from both SQL and JSON in some minor details. For
|
||||
example, SQL/JSON path allows <literal>.1</literal> and
|
||||
<literal>1.</literal>, which are invalid in JSON. Non-decimal integer
|
||||
literals and underscore separators are supported, for example,
|
||||
<literal>1_000_000</literal>, <literal>0x1EEE_FFFF</literal>,
|
||||
<literal>0o273</literal>, <literal>0b100101</literal>. In SQL/JSON path
|
||||
(and in JavaScript, but not in SQL proper), there must not be an underscore
|
||||
separator directly after the radix prefix.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
An SQL/JSON path expression is typically written in an SQL query as an
|
||||
SQL character string literal, so it must be enclosed in single quotes,
|
||||
|
@ -553,6 +553,7 @@ T836 SQL/JSON path language: starts with predicate YES
|
||||
T837 SQL/JSON path language: regex_like predicate YES
|
||||
T838 JSON_TABLE: PLAN DEFAULT clause NO
|
||||
T839 Formatted cast of datetimes to/from character strings NO
|
||||
T840 Hex integer literals in SQL/JSON path language YES SQL:202x draft
|
||||
M001 Datalinks NO
|
||||
M002 Datalinks via SQL/CLI NO
|
||||
M003 Datalinks via Embedded SQL NO
|
||||
|
@ -90,21 +90,32 @@ blank [ \t\n\r\f]
|
||||
/* "other" means anything that's not special, blank, or '\' or '"' */
|
||||
other [^\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/\\\" \t\n\r\f]
|
||||
|
||||
digit [0-9]
|
||||
integer (0|[1-9]{digit}*)
|
||||
decimal ({integer}\.{digit}*|\.{digit}+)
|
||||
real ({integer}|{decimal})[Ee][-+]?{digit}+
|
||||
realfail ({integer}|{decimal})[Ee][-+]
|
||||
decdigit [0-9]
|
||||
hexdigit [0-9A-Fa-f]
|
||||
octdigit [0-7]
|
||||
bindigit [0-1]
|
||||
|
||||
integer_junk {integer}{other}
|
||||
/* DecimalInteger in ECMAScript; must not start with 0 unless it's exactly 0 */
|
||||
decinteger (0|[1-9](_?{decdigit})*)
|
||||
/* DecimalDigits in ECMAScript; only used as part of other rules */
|
||||
decdigits {decdigit}(_?{decdigit})*
|
||||
/* Non-decimal integers; in ECMAScript, these must not have underscore after prefix */
|
||||
hexinteger 0[xX]{hexdigit}(_?{hexdigit})*
|
||||
octinteger 0[oO]{octdigit}(_?{octdigit})*
|
||||
bininteger 0[bB]{bindigit}(_?{bindigit})*
|
||||
|
||||
decimal ({decinteger}\.{decdigits}?|\.{decdigits})
|
||||
real ({decinteger}|{decimal})[Ee][-+]?{decdigits}
|
||||
realfail ({decinteger}|{decimal})[Ee][-+]
|
||||
|
||||
decinteger_junk {decinteger}{other}
|
||||
decimal_junk {decimal}{other}
|
||||
real_junk {real}{other}
|
||||
|
||||
hex_dig [0-9A-Fa-f]
|
||||
unicode \\u({hex_dig}{4}|\{{hex_dig}{1,6}\})
|
||||
unicodefail \\u({hex_dig}{0,3}|\{{hex_dig}{0,6})
|
||||
hex_char \\x{hex_dig}{2}
|
||||
hex_fail \\x{hex_dig}{0,1}
|
||||
unicode \\u({hexdigit}{4}|\{{hexdigit}{1,6}\})
|
||||
unicodefail \\u({hexdigit}{0,3}|\{{hexdigit}{0,6})
|
||||
hex_char \\x{hexdigit}{2}
|
||||
hex_fail \\x{hexdigit}{0,1}
|
||||
|
||||
%%
|
||||
|
||||
@ -274,7 +285,28 @@ hex_fail \\x{hex_dig}{0,1}
|
||||
return NUMERIC_P;
|
||||
}
|
||||
|
||||
{integer} {
|
||||
{decinteger} {
|
||||
addstring(true, yytext, yyleng);
|
||||
addchar(false, '\0');
|
||||
yylval->str = scanstring;
|
||||
return INT_P;
|
||||
}
|
||||
|
||||
{hexinteger} {
|
||||
addstring(true, yytext, yyleng);
|
||||
addchar(false, '\0');
|
||||
yylval->str = scanstring;
|
||||
return INT_P;
|
||||
}
|
||||
|
||||
{octinteger} {
|
||||
addstring(true, yytext, yyleng);
|
||||
addchar(false, '\0');
|
||||
yylval->str = scanstring;
|
||||
return INT_P;
|
||||
}
|
||||
|
||||
{bininteger} {
|
||||
addstring(true, yytext, yyleng);
|
||||
addchar(false, '\0');
|
||||
yylval->str = scanstring;
|
||||
@ -287,7 +319,7 @@ hex_fail \\x{hex_dig}{0,1}
|
||||
"invalid numeric literal");
|
||||
yyterminate();
|
||||
}
|
||||
{integer_junk} {
|
||||
{decinteger_junk} {
|
||||
jsonpath_yyerror(
|
||||
NULL, escontext,
|
||||
"trailing junk after numeric literal");
|
||||
|
@ -836,6 +836,7 @@ select '$ ? (@.a < +10.1e+1)'::jsonpath;
|
||||
$?(@."a" < 101)
|
||||
(1 row)
|
||||
|
||||
-- numeric literals
|
||||
select '0'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
@ -846,6 +847,10 @@ select '00'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "00" of jsonpath input
|
||||
LINE 1: select '00'::jsonpath;
|
||||
^
|
||||
select '0755'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '0755'::jsonpath;
|
||||
^
|
||||
select '0.0'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
@ -1032,6 +1037,163 @@ select '1?(2>3)'::jsonpath;
|
||||
(1)?(2 > 3)
|
||||
(1 row)
|
||||
|
||||
-- nondecimal
|
||||
select '0b100101'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
37
|
||||
(1 row)
|
||||
|
||||
select '0o273'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
187
|
||||
(1 row)
|
||||
|
||||
select '0x42F'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
1071
|
||||
(1 row)
|
||||
|
||||
-- error cases
|
||||
select '0b'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "0b" of jsonpath input
|
||||
LINE 1: select '0b'::jsonpath;
|
||||
^
|
||||
select '1b'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "1b" of jsonpath input
|
||||
LINE 1: select '1b'::jsonpath;
|
||||
^
|
||||
select '0b0x'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '0b0x'::jsonpath;
|
||||
^
|
||||
select '0o'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "0o" of jsonpath input
|
||||
LINE 1: select '0o'::jsonpath;
|
||||
^
|
||||
select '1o'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "1o" of jsonpath input
|
||||
LINE 1: select '1o'::jsonpath;
|
||||
^
|
||||
select '0o0x'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '0o0x'::jsonpath;
|
||||
^
|
||||
select '0x'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "0x" of jsonpath input
|
||||
LINE 1: select '0x'::jsonpath;
|
||||
^
|
||||
select '1x'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "1x" of jsonpath input
|
||||
LINE 1: select '1x'::jsonpath;
|
||||
^
|
||||
select '0x0y'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '0x0y'::jsonpath;
|
||||
^
|
||||
-- underscores
|
||||
select '1_000_000'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
1000000
|
||||
(1 row)
|
||||
|
||||
select '1_2_3'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
123
|
||||
(1 row)
|
||||
|
||||
select '0x1EEE_FFFF'::jsonpath;
|
||||
jsonpath
|
||||
-----------
|
||||
518979583
|
||||
(1 row)
|
||||
|
||||
select '0o2_73'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
187
|
||||
(1 row)
|
||||
|
||||
select '0b10_0101'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
37
|
||||
(1 row)
|
||||
|
||||
select '1_000.000_005'::jsonpath;
|
||||
jsonpath
|
||||
-------------
|
||||
1000.000005
|
||||
(1 row)
|
||||
|
||||
select '1_000.'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
1000
|
||||
(1 row)
|
||||
|
||||
select '.000_005'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
0.000005
|
||||
(1 row)
|
||||
|
||||
select '1_000.5e0_1'::jsonpath;
|
||||
jsonpath
|
||||
----------
|
||||
10005
|
||||
(1 row)
|
||||
|
||||
-- error cases
|
||||
select '_100'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '_100'::jsonpath;
|
||||
^
|
||||
select '100_'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "100_" of jsonpath input
|
||||
LINE 1: select '100_'::jsonpath;
|
||||
^
|
||||
select '100__000'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '100__000'::jsonpath;
|
||||
^
|
||||
select '_1_000.5'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '_1_000.5'::jsonpath;
|
||||
^
|
||||
select '1_000_.5'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "1_000_" of jsonpath input
|
||||
LINE 1: select '1_000_.5'::jsonpath;
|
||||
^
|
||||
select '1_000._5'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "1_000._" of jsonpath input
|
||||
LINE 1: select '1_000._5'::jsonpath;
|
||||
^
|
||||
select '1_000.5_'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "1_000.5_" of jsonpath input
|
||||
LINE 1: select '1_000.5_'::jsonpath;
|
||||
^
|
||||
select '1_000.5e_1'::jsonpath;
|
||||
ERROR: trailing junk after numeric literal at or near "1_000.5e" of jsonpath input
|
||||
LINE 1: select '1_000.5e_1'::jsonpath;
|
||||
^
|
||||
-- underscore after prefix not allowed in JavaScript (but allowed in SQL)
|
||||
select '0b_10_0101'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '0b_10_0101'::jsonpath;
|
||||
^
|
||||
select '0o_273'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '0o_273'::jsonpath;
|
||||
^
|
||||
select '0x_42F'::jsonpath;
|
||||
ERROR: syntax error at end of jsonpath input
|
||||
LINE 1: select '0x_42F'::jsonpath;
|
||||
^
|
||||
-- test non-error-throwing API
|
||||
SELECT str as jsonpath,
|
||||
pg_input_is_valid(str,'jsonpath') as ok,
|
||||
|
@ -152,8 +152,11 @@ select '$ ? (@.a < 10.1e+1)'::jsonpath;
|
||||
select '$ ? (@.a < -10.1e+1)'::jsonpath;
|
||||
select '$ ? (@.a < +10.1e+1)'::jsonpath;
|
||||
|
||||
-- numeric literals
|
||||
|
||||
select '0'::jsonpath;
|
||||
select '00'::jsonpath;
|
||||
select '0755'::jsonpath;
|
||||
select '0.0'::jsonpath;
|
||||
select '0.000'::jsonpath;
|
||||
select '0.000e1'::jsonpath;
|
||||
@ -188,6 +191,53 @@ select '(1.).e'::jsonpath;
|
||||
select '(1.).e3'::jsonpath;
|
||||
select '1?(2>3)'::jsonpath;
|
||||
|
||||
-- nondecimal
|
||||
select '0b100101'::jsonpath;
|
||||
select '0o273'::jsonpath;
|
||||
select '0x42F'::jsonpath;
|
||||
|
||||
-- error cases
|
||||
select '0b'::jsonpath;
|
||||
select '1b'::jsonpath;
|
||||
select '0b0x'::jsonpath;
|
||||
|
||||
select '0o'::jsonpath;
|
||||
select '1o'::jsonpath;
|
||||
select '0o0x'::jsonpath;
|
||||
|
||||
select '0x'::jsonpath;
|
||||
select '1x'::jsonpath;
|
||||
select '0x0y'::jsonpath;
|
||||
|
||||
-- underscores
|
||||
select '1_000_000'::jsonpath;
|
||||
select '1_2_3'::jsonpath;
|
||||
select '0x1EEE_FFFF'::jsonpath;
|
||||
select '0o2_73'::jsonpath;
|
||||
select '0b10_0101'::jsonpath;
|
||||
|
||||
select '1_000.000_005'::jsonpath;
|
||||
select '1_000.'::jsonpath;
|
||||
select '.000_005'::jsonpath;
|
||||
select '1_000.5e0_1'::jsonpath;
|
||||
|
||||
-- error cases
|
||||
select '_100'::jsonpath;
|
||||
select '100_'::jsonpath;
|
||||
select '100__000'::jsonpath;
|
||||
|
||||
select '_1_000.5'::jsonpath;
|
||||
select '1_000_.5'::jsonpath;
|
||||
select '1_000._5'::jsonpath;
|
||||
select '1_000.5_'::jsonpath;
|
||||
select '1_000.5e_1'::jsonpath;
|
||||
|
||||
-- underscore after prefix not allowed in JavaScript (but allowed in SQL)
|
||||
select '0b_10_0101'::jsonpath;
|
||||
select '0o_273'::jsonpath;
|
||||
select '0x_42F'::jsonpath;
|
||||
|
||||
|
||||
-- test non-error-throwing API
|
||||
|
||||
SELECT str as jsonpath,
|
||||
|
Loading…
x
Reference in New Issue
Block a user