Make JSON path numeric literals more correct

Per ECMAScript standard (ECMA-262, referenced by SQL standard), the
syntax forms

.1
1.

should be allowed for decimal numeric literals, but the existing
implementation rejected them.

Also, by the same standard, reject trailing junk after numeric
literals.

Note that the ECMAScript standard for numeric literals is in respects
like these slightly different from the JSON standard, which might be
the original cause for this discrepancy.

A change is that this kind of syntax is now rejected:

    1.type()

This needs to be written as

    (1).type()

This is correct; normal JavaScript also does not accept this syntax.

We also need to fix up the jsonpath output function for this case.  We
put parentheses around numeric items if they are followed by another
path item.

Reviewed-by: Nikita Glukhov <n.gluhov@postgrespro.ru>
Discussion: https://www.postgresql.org/message-id/flat/50a828cc-0a00-7791-7883-2ed06dfb2dbb@enterprisedb.com
This commit is contained in:
Peter Eisentraut 2022-03-28 10:41:43 +02:00
parent 91c0570a79
commit e26114c817
4 changed files with 177 additions and 99 deletions

View File

@ -500,9 +500,13 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
escape_json(buf, jspGetString(v, NULL));
break;
case jpiNumeric:
if (jspHasNext(v))
appendStringInfoChar(buf, '(');
appendStringInfoString(buf,
DatumGetCString(DirectFunctionCall1(numeric_out,
NumericGetDatum(jspGetNumeric(v)))));
if (jspHasNext(v))
appendStringInfoChar(buf, ')');
break;
case jpiBool:
if (jspGetBool(v))

View File

@ -82,11 +82,13 @@ other [^\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/\\\" \t\n\r\f]
digit [0-9]
integer (0|[1-9]{digit}*)
decimal {integer}\.{digit}+
decimalfail {integer}\.
decimal ({integer}\.{digit}*|\.{digit}+)
real ({integer}|{decimal})[Ee][-+]?{digit}+
realfail1 ({integer}|{decimal})[Ee]
realfail2 ({integer}|{decimal})[Ee][-+]
realfail ({integer}|{decimal})[Ee][-+]
integer_junk {integer}{other}
decimal_junk {decimal}{other}
real_junk {real}{other}
hex_dig [0-9A-Fa-f]
unicode \\u({hex_dig}{4}|\{{hex_dig}{1,6}\})
@ -242,16 +244,10 @@ hex_fail \\x{hex_dig}{0,1}
return INT_P;
}
{decimalfail} {
/* throw back the ., and treat as integer */
yyless(yyleng - 1);
addstring(true, yytext, yyleng);
addchar(false, '\0');
yylval->str = scanstring;
return INT_P;
}
({realfail1}|{realfail2}) { yyerror(NULL, "invalid floating point number"); }
{realfail} { yyerror(NULL, "invalid numeric literal"); }
{integer_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
{decimal_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
{real_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
\" {
addchar(true, '\0');

View File

@ -354,21 +354,19 @@ select 'null.type()'::jsonpath;
(1 row)
select '1.type()'::jsonpath;
jsonpath
----------
1.type()
(1 row)
ERROR: trailing junk after numeric literal at or near "1.t" of jsonpath input
LINE 1: select '1.type()'::jsonpath;
^
select '(1).type()'::jsonpath;
jsonpath
----------
1.type()
jsonpath
------------
(1).type()
(1 row)
select '1.2.type()'::jsonpath;
jsonpath
------------
1.2.type()
jsonpath
--------------
(1.2).type()
(1 row)
select '"aaa".type()'::jsonpath;
@ -545,9 +543,9 @@ select '(($))'::jsonpath;
(1 row)
select '((($ + 1)).a + ((2)).b ? ((((@ > 1)) || (exists(@.c)))))'::jsonpath;
jsonpath
-------------------------------------------------
(($ + 1)."a" + 2."b"?(@ > 1 || exists (@."c")))
jsonpath
---------------------------------------------------
(($ + 1)."a" + (2)."b"?(@ > 1 || exists (@."c")))
(1 row)
select '$ ? (@.a < 1)'::jsonpath;
@ -569,17 +567,23 @@ select '$ ? (@.a < +1)'::jsonpath;
(1 row)
select '$ ? (@.a < .1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < .1)'::jsonpath;
^
jsonpath
-----------------
$?(@."a" < 0.1)
(1 row)
select '$ ? (@.a < -.1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < -.1)'::jsonpath;
^
jsonpath
------------------
$?(@."a" < -0.1)
(1 row)
select '$ ? (@.a < +.1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < +.1)'::jsonpath;
^
jsonpath
-----------------
$?(@."a" < 0.1)
(1 row)
select '$ ? (@.a < 0.1)'::jsonpath;
jsonpath
-----------------
@ -635,17 +639,23 @@ select '$ ? (@.a < +1e1)'::jsonpath;
(1 row)
select '$ ? (@.a < .1e1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < .1e1)'::jsonpath;
^
jsonpath
---------------
$?(@."a" < 1)
(1 row)
select '$ ? (@.a < -.1e1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < -.1e1)'::jsonpath;
^
jsonpath
----------------
$?(@."a" < -1)
(1 row)
select '$ ? (@.a < +.1e1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < +.1e1)'::jsonpath;
^
jsonpath
---------------
$?(@."a" < 1)
(1 row)
select '$ ? (@.a < 0.1e1)'::jsonpath;
jsonpath
---------------
@ -701,17 +711,23 @@ select '$ ? (@.a < +1e-1)'::jsonpath;
(1 row)
select '$ ? (@.a < .1e-1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < .1e-1)'::jsonpath;
^
jsonpath
------------------
$?(@."a" < 0.01)
(1 row)
select '$ ? (@.a < -.1e-1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < -.1e-1)'::jsonpath;
^
jsonpath
-------------------
$?(@."a" < -0.01)
(1 row)
select '$ ? (@.a < +.1e-1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < +.1e-1)'::jsonpath;
^
jsonpath
------------------
$?(@."a" < 0.01)
(1 row)
select '$ ? (@.a < 0.1e-1)'::jsonpath;
jsonpath
------------------
@ -767,17 +783,23 @@ select '$ ? (@.a < +1e+1)'::jsonpath;
(1 row)
select '$ ? (@.a < .1e+1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < .1e+1)'::jsonpath;
^
jsonpath
---------------
$?(@."a" < 1)
(1 row)
select '$ ? (@.a < -.1e+1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < -.1e+1)'::jsonpath;
^
jsonpath
----------------
$?(@."a" < -1)
(1 row)
select '$ ? (@.a < +.1e+1)'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '$ ? (@.a < +.1e+1)'::jsonpath;
^
jsonpath
---------------
$?(@."a" < 1)
(1 row)
select '$ ? (@.a < 0.1e+1)'::jsonpath;
jsonpath
---------------
@ -821,7 +843,7 @@ select '0'::jsonpath;
(1 row)
select '00'::jsonpath;
ERROR: syntax error, unexpected IDENT_P at end of jsonpath input
ERROR: trailing junk after numeric literal at or near "00" of jsonpath input
LINE 1: select '00'::jsonpath;
^
select '0.0'::jsonpath;
@ -878,30 +900,60 @@ select '0.0010e+2'::jsonpath;
0.10
(1 row)
select '.001'::jsonpath;
jsonpath
----------
0.001
(1 row)
select '.001e1'::jsonpath;
jsonpath
----------
0.01
(1 row)
select '1.'::jsonpath;
jsonpath
----------
1
(1 row)
select '1.e1'::jsonpath;
jsonpath
----------
10
(1 row)
select '1a'::jsonpath;
ERROR: trailing junk after numeric literal at or near "1a" of jsonpath input
LINE 1: select '1a'::jsonpath;
^
select '1e'::jsonpath;
ERROR: invalid floating point number at or near "1e" of jsonpath input
ERROR: trailing junk after numeric literal at or near "1e" of jsonpath input
LINE 1: select '1e'::jsonpath;
^
select '1.e'::jsonpath;
jsonpath
----------
1."e"
(1 row)
ERROR: trailing junk after numeric literal at or near "1.e" of jsonpath input
LINE 1: select '1.e'::jsonpath;
^
select '1.2a'::jsonpath;
ERROR: trailing junk after numeric literal at or near "1.2a" of jsonpath input
LINE 1: select '1.2a'::jsonpath;
^
select '1.2e'::jsonpath;
ERROR: invalid floating point number at or near "1.2e" of jsonpath input
ERROR: trailing junk after numeric literal at or near "1.2e" of jsonpath input
LINE 1: select '1.2e'::jsonpath;
^
select '1.2.e'::jsonpath;
jsonpath
----------
1.2."e"
jsonpath
-----------
(1.2)."e"
(1 row)
select '(1.2).e'::jsonpath;
jsonpath
----------
1.2."e"
jsonpath
-----------
(1.2)."e"
(1 row)
select '1e3'::jsonpath;
@ -913,19 +965,19 @@ select '1e3'::jsonpath;
select '1.e3'::jsonpath;
jsonpath
----------
1."e3"
1000
(1 row)
select '1.e3.e'::jsonpath;
jsonpath
------------
1."e3"."e"
(1000)."e"
(1 row)
select '1.e3.e4'::jsonpath;
jsonpath
-------------
1."e3"."e4"
(1000)."e4"
(1 row)
select '1.2e3'::jsonpath;
@ -934,31 +986,49 @@ select '1.2e3'::jsonpath;
1200
(1 row)
select '1.2e3a'::jsonpath;
ERROR: trailing junk after numeric literal at or near "1.2e3a" of jsonpath input
LINE 1: select '1.2e3a'::jsonpath;
^
select '1.2.e3'::jsonpath;
jsonpath
----------
1.2."e3"
jsonpath
------------
(1.2)."e3"
(1 row)
select '(1.2).e3'::jsonpath;
jsonpath
----------
1.2."e3"
jsonpath
------------
(1.2)."e3"
(1 row)
select '1..e'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '1..e'::jsonpath;
^
jsonpath
----------
(1)."e"
(1 row)
select '1..e3'::jsonpath;
ERROR: syntax error, unexpected '.' at or near "." of jsonpath input
LINE 1: select '1..e3'::jsonpath;
^
jsonpath
----------
(1)."e3"
(1 row)
select '(1.).e'::jsonpath;
ERROR: syntax error, unexpected ')' at or near ")" of jsonpath input
LINE 1: select '(1.).e'::jsonpath;
^
jsonpath
----------
(1)."e"
(1 row)
select '(1.).e3'::jsonpath;
ERROR: syntax error, unexpected ')' at or near ")" of jsonpath input
LINE 1: select '(1.).e3'::jsonpath;
^
jsonpath
----------
(1)."e3"
(1 row)
select '1?(2>3)'::jsonpath;
jsonpath
-------------
(1)?(2 > 3)
(1 row)

View File

@ -163,8 +163,14 @@ select '0.0010'::jsonpath;
select '0.0010e-1'::jsonpath;
select '0.0010e+1'::jsonpath;
select '0.0010e+2'::jsonpath;
select '.001'::jsonpath;
select '.001e1'::jsonpath;
select '1.'::jsonpath;
select '1.e1'::jsonpath;
select '1a'::jsonpath;
select '1e'::jsonpath;
select '1.e'::jsonpath;
select '1.2a'::jsonpath;
select '1.2e'::jsonpath;
select '1.2.e'::jsonpath;
select '(1.2).e'::jsonpath;
@ -173,9 +179,11 @@ select '1.e3'::jsonpath;
select '1.e3.e'::jsonpath;
select '1.e3.e4'::jsonpath;
select '1.2e3'::jsonpath;
select '1.2e3a'::jsonpath;
select '1.2.e3'::jsonpath;
select '(1.2).e3'::jsonpath;
select '1..e'::jsonpath;
select '1..e3'::jsonpath;
select '(1.).e'::jsonpath;
select '(1.).e3'::jsonpath;
select '1?(2>3)'::jsonpath;