On 06.03.22 02:43, Nikita Glukhov wrote:
Obviously, there are compatibility issues with expressions like
'1.type()', which will start to require parentheses around numbers,
but they seem to be useful only for our regression tests.

The corresponding changes in jsonpath_out() related to parentheses
are missing in the v2 patch:

=# select '(1).a'::jsonpath;
  jsonpath
----------
  1."a"
(1 row)

=# select '(1).a'::jsonpath::text::jsonpath;
ERROR:  syntax error, unexpected STRING_P, expecting $end at or near """ of 
jsonpath input


I have added in v3 enclosing of numbers in parentheses if they have
successive path items. (Changed results of several test cases, one test
case added.)

Thank you for these insights. I have integrated this into my patch and updated the commit message to point out the change.
From cbe9c601ed5f0db894df74377bbc5aa624626c4e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Wed, 16 Mar 2022 18:24:00 +0100
Subject: [PATCH v4] 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.

Discussion: 
https://www.postgresql.org/message-id/flat/50a828cc-0a00-7791-7883-2ed06dfb2...@enterprisedb.com
---
 src/backend/utils/adt/jsonpath.c       |   4 +
 src/backend/utils/adt/jsonpath_scan.l  |  24 ++-
 src/test/regress/expected/jsonpath.out | 238 ++++++++++++++++---------
 src/test/regress/sql/jsonpath.sql      |   8 +
 4 files changed, 176 insertions(+), 98 deletions(-)

diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 9be4e305ff..91af030095 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -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))
diff --git a/src/backend/utils/adt/jsonpath_scan.l 
b/src/backend/utils/adt/jsonpath_scan.l
index 827a9e44cb..1f08e7c51f 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -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');
diff --git a/src/test/regress/expected/jsonpath.out 
b/src/test/regress/expected/jsonpath.out
index e399fa9631..88eb22a4e9 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -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 '1e'::jsonpath;
-ERROR:  invalid floating point number at or near "1e" of jsonpath input
-LINE 1: select '1e'::jsonpath;
-               ^
-select '1.e'::jsonpath;
+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 
 ----------
- 1."e"
+ 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:  trailing junk after numeric literal at or near "1e" of jsonpath input
+LINE 1: select '1e'::jsonpath;
+               ^
+select '1.e'::jsonpath;
+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"
+(1 row)
+
+select '1..e'::jsonpath;
  jsonpath 
 ----------
- 1.2."e3"
+ (1)."e"
 (1 row)
 
-select '1..e'::jsonpath;
-ERROR:  syntax error, unexpected '.' at or near "." of jsonpath input
-LINE 1: select '1..e'::jsonpath;
-               ^
 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)
+
diff --git a/src/test/regress/sql/jsonpath.sql 
b/src/test/regress/sql/jsonpath.sql
index 17ab775783..d491714614 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -163,8 +163,14 @@
 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.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;
-- 
2.35.1

Reply via email to