From c88047f7fb45363e836aef0eb2422099106e544b Mon Sep 17 00:00:00 2001
From: Alexandra Wang <alexandra.wang.oss@gmail.com>
Date: Mon, 10 Mar 2025 07:00:03 -0500
Subject: [PATCH v11 8/8] Add "**" as a new token in scanners

DOUBLE_ASTERISK is now recognized but not yet implemented.

This change improves error message readability, as "**" is supported
in jsonpath_scan.l as ANY_P.  However, the SQL standard for simplified
JSON accessors does not specify "**", so it is not supported in that
context.
---
 src/backend/parser/gram.y                            |  9 ++++++++-
 src/backend/parser/scan.l                            |  6 ++++++
 src/include/parser/scanner.h                         |  2 +-
 src/interfaces/ecpg/preproc/pgc.l                    |  5 +++++
 src/interfaces/ecpg/test/expected/sql-sqljson.c      | 12 +++++++++++-
 src/interfaces/ecpg/test/expected/sql-sqljson.stderr | 11 +++++++++++
 src/interfaces/ecpg/test/sql/sqljson.pgc             |  3 +++
 src/pl/plpgsql/src/pl_gram.y                         |  2 +-
 src/test/regress/expected/jsonb.out                  |  2 +-
 9 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 44840bc1b25..4635616d93a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -687,7 +687,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  */
 %token <str>	IDENT UIDENT FCONST SCONST USCONST BCONST XCONST Op
 %token <ival>	ICONST PARAM
-%token			TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER
+%token			TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER DOUBLE_ASTERISK
 %token			LESS_EQUALS GREATER_EQUALS NOT_EQUALS
 
 /*
@@ -16969,6 +16969,13 @@ indirection_el:
 				{
 					$$ = (Node *) makeNode(A_Star);
 				}
+			| '.' DOUBLE_ASTERISK
+				{
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("arbitrary depth wild card in simple json accessor not supported"),
+					parser_errposition(@2)));
+				}
 			| '[' a_expr ']'
 				{
 					A_Indices *ai = makeNode(A_Indices);
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index 08990831fe8..c58ba233153 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -338,6 +338,7 @@ identifier		{ident_start}{ident_cont}*
 typecast		"::"
 dot_dot			\.\.
 colon_equals	":="
+double_asterisk	"**"
 
 /*
  * These operator-like tokens (unlike the above ones) also match the {operator}
@@ -851,6 +852,11 @@ other			.
 					return COLON_EQUALS;
 				}
 
+{double_asterisk}	{
+						SET_YYLLOC();
+						return DOUBLE_ASTERISK;
+					}
+
 {equals_greater} {
 					SET_YYLLOC();
 					return EQUALS_GREATER;
diff --git a/src/include/parser/scanner.h b/src/include/parser/scanner.h
index 74ad86698ac..2f9b7baa1a9 100644
--- a/src/include/parser/scanner.h
+++ b/src/include/parser/scanner.h
@@ -50,7 +50,7 @@ typedef union core_YYSTYPE
  * the ASCII characters plus these:
  *	%token <str>	IDENT UIDENT FCONST SCONST USCONST BCONST XCONST Op
  *	%token <ival>	ICONST PARAM
- *	%token			TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER
+ *	%token			TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER DOUBLE_ASTERISK
  *	%token			LESS_EQUALS GREATER_EQUALS NOT_EQUALS
  * The above token definitions *must* be the first ones declared in any
  * bison parser built atop this scanner, so that they will have consistent
diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l
index 63283a4a1e5..1415cbe2808 100644
--- a/src/interfaces/ecpg/preproc/pgc.l
+++ b/src/interfaces/ecpg/preproc/pgc.l
@@ -321,6 +321,7 @@ array			({ident_cont}|{whitespace}|[\[\]\+\-\*\%\/\(\)\>\.])*
 typecast		"::"
 dot_dot			\.\.
 colon_equals	":="
+double_asterisk	"**"
 
 /*
  * These operator-like tokens (unlike the above ones) also match the {operator}
@@ -832,6 +833,10 @@ cppline			{space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
 					return COLON_EQUALS;
 				}
 
+{double_asterisk}	{
+						return DOUBLE_ASTERISK;
+					}
+
 {equals_greater} {
 					return EQUALS_GREATER;
 				}
diff --git a/src/interfaces/ecpg/test/expected/sql-sqljson.c b/src/interfaces/ecpg/test/expected/sql-sqljson.c
index 748b2e2bee6..f772305c209 100644
--- a/src/interfaces/ecpg/test/expected/sql-sqljson.c
+++ b/src/interfaces/ecpg/test/expected/sql-sqljson.c
@@ -527,12 +527,22 @@ if (sqlca.sqlcode < 0) sqlprint();}
 
 	printf("Found json=%s\n", json);
 
-  { ECPGdisconnect(__LINE__, "CURRENT");
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json ( ( '{\"a\": {\"b\": 1, \"c\": 2}, \"b\": [{\"x\": 1}, {\"x\": [12, {\"y\":1}]}]}' :: jsonb ) . ** . b )", ECPGt_EOIT, 
+	ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
 #line 151 "sqljson.pgc"
 
 if (sqlca.sqlcode < 0) sqlprint();}
 #line 151 "sqljson.pgc"
 
+	// error
+
+  { ECPGdisconnect(__LINE__, "CURRENT");
+#line 154 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 154 "sqljson.pgc"
+
 
   return 0;
 }
diff --git a/src/interfaces/ecpg/test/expected/sql-sqljson.stderr b/src/interfaces/ecpg/test/expected/sql-sqljson.stderr
index 92c5d1520c4..d9a2fe21915 100644
--- a/src/interfaces/ecpg/test/expected/sql-sqljson.stderr
+++ b/src/interfaces/ecpg/test/expected/sql-sqljson.stderr
@@ -359,5 +359,16 @@ SQL error: schema "jsonb" does not exist on line 121
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_get_data on line 148: RESULT: [1, [12, {"y": 1}]] offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 151: query: select json ( ( '{"a": {"b": 1, "c": 2}, "b": [{"x": 1}, {"x": [12, {"y":1}]}]}' :: jsonb ) . ** . b ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 151: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_check_PQresult on line 151: bad response - ERROR:  arbitrary depth wild card in simple json accessor not supported
+LINE 1: ...b": [{"x": 1}, {"x": [12, {"y":1}]}]}' :: jsonb ) . ** . b )
+                                                               ^
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlstate 0A000 (sqlcode -400): arbitrary depth wild card in simple json accessor not supported on line 151
+[NO_PID]: sqlca: code: -400, state: 0A000
+SQL error: arbitrary depth wild card in simple json accessor not supported on line 151
 [NO_PID]: ecpg_finish: connection ecpg1_regression closed
 [NO_PID]: sqlca: code: 0, state: 00000
diff --git a/src/interfaces/ecpg/test/sql/sqljson.pgc b/src/interfaces/ecpg/test/sql/sqljson.pgc
index 4e7427d237d..65443d30055 100644
--- a/src/interfaces/ecpg/test/sql/sqljson.pgc
+++ b/src/interfaces/ecpg/test/sql/sqljson.pgc
@@ -148,6 +148,9 @@ EXEC SQL END DECLARE SECTION;
 	EXEC SQL SELECT JSON(('{"a": {"b": 1, "c": 2}, "b": [{"x": 1}, {"x": [12, {"y":1}]}]}'::jsonb).*.x) INTO :json;
 	printf("Found json=%s\n", json);
 
+	EXEC SQL SELECT JSON(('{"a": {"b": 1, "c": 2}, "b": [{"x": 1}, {"x": [12, {"y":1}]}]}'::jsonb).**.b) INTO :json;
+	// error
+
   EXEC SQL DISCONNECT;
 
   return 0;
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 5612e66d023..13e06ad5b0b 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -245,7 +245,7 @@ static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
  */
 %token <str>	IDENT UIDENT FCONST SCONST USCONST BCONST XCONST Op
 %token <ival>	ICONST PARAM
-%token			TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER
+%token			TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER DOUBLE_ASTERISK
 %token			LESS_EQUALS GREATER_EQUALS NOT_EQUALS
 
 /*
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index 1a9452937d5..0729d7251c6 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -6181,7 +6181,7 @@ SELECT (jb).a.b.c.* FROM test_jsonb_dot_notation;
 (1 row)
 
 SELECT (jb).a.**.x FROM test_jsonb_dot_notation; -- not supported
-ERROR:  syntax error at or near "**"
+ERROR:  arbitrary depth wild card in simple json accessor not supported
 LINE 1: SELECT (jb).a.**.x FROM test_jsonb_dot_notation;
                       ^
 -- explains should work
-- 
2.39.5 (Apple Git-154)

