From f21d34f62e238560cb3b5d3e148e8a018249fc64 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Wed, 6 Dec 2023 22:29:13 +0900
Subject: [PATCH v31 7/7] JSON_TABLE: don't assign precedence to NESTED, PATH

---
 src/backend/parser/gram.y            | 37 ++++++++++++++++++++--------
 src/backend/parser/parser.c          | 11 +++++++++
 src/interfaces/ecpg/preproc/parse.pl |  1 +
 src/interfaces/ecpg/preproc/parser.c | 11 +++++++++
 4 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1fee378c5c..dcab3f270a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -815,7 +815,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  * FORMAT_LA, NULLS_LA, WITH_LA, and WITHOUT_LA are needed to make the grammar
  * LALR(1).
  */
-%token		FORMAT_LA NOT_LA NULLS_LA WITH_LA WITHOUT_LA
+%token		FORMAT_LA NESTED_LA NOT_LA NULLS_LA WITH_LA WITHOUT_LA
 
 /*
  * The grammar likewise thinks these tokens are keywords, but they are never
@@ -909,8 +909,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  */
 %left		JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
 
-%nonassoc	NESTED
-%left		PATH
 %%
 
 /*
@@ -16786,7 +16784,7 @@ json_table_column_definition:
 					n->location = @1;
 					$$ = (Node *) n;
 				}
-			| NESTED path_opt Sconst
+			| NESTED_LA PATH Sconst
 				COLUMNS '(' json_table_column_definition_list ')'
 				{
 					JsonTableColumn *n = makeNode(JsonTableColumn);
@@ -16798,7 +16796,7 @@ json_table_column_definition:
 					n->location = @1;
 					$$ = (Node *) n;
 				}
-			| NESTED path_opt Sconst AS name
+			| NESTED_LA PATH Sconst AS name
 				COLUMNS '(' json_table_column_definition_list ')'
 				{
 					JsonTableColumn *n = makeNode(JsonTableColumn);
@@ -16810,6 +16808,30 @@ json_table_column_definition:
 					n->location = @1;
 					$$ = (Node *) n;
 				}
+			| NESTED Sconst
+				COLUMNS '('	json_table_column_definition_list ')'
+				{
+					JsonTableColumn *n = makeNode(JsonTableColumn);
+
+					n->coltype = JTC_NESTED;
+					n->pathspec = $2;
+					n->pathname = NULL;
+					n->columns = $5;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
+			| NESTED Sconst AS name
+				COLUMNS '('	json_table_column_definition_list ')'
+				{
+					JsonTableColumn *n = makeNode(JsonTableColumn);
+
+					n->coltype = JTC_NESTED;
+					n->pathspec = $2;
+					n->pathname = $4;
+					n->columns = $7;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
 		;
 
 json_table_column_path_specification_clause_opt:
@@ -16817,11 +16839,6 @@ json_table_column_path_specification_clause_opt:
 			| /* EMPTY */							{ $$ = NULL; }
 		;
 
-path_opt:
-			PATH								{ }
-			| /* EMPTY */						{ }
-		;
-
 json_table_plan_clause_opt:
 			PLAN '(' json_table_plan ')'			{ $$ = $3; }
 			| PLAN DEFAULT '(' json_table_default_plan_choices ')'
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index e17c310cc1..e3092f2c3e 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -138,6 +138,7 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
 	switch (cur_token)
 	{
 		case FORMAT:
+		case NESTED:
 			cur_token_length = 6;
 			break;
 		case NOT:
@@ -204,6 +205,16 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
 			}
 			break;
 
+		case NESTED:
+			/* Replace NESTED by NESTED_LA if it's followed by PATH */
+			switch (next_token)
+			{
+				case PATH:
+					cur_token = NESTED_LA;
+					break;
+			}
+			break;
+
 		case NOT:
 			/* Replace NOT by NOT_LA if it's followed by BETWEEN, IN, etc */
 			switch (next_token)
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index 7574fc3110..d27fc7c87d 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -56,6 +56,7 @@ my %replace_token = (
 # or in the block
 my %replace_string = (
 	'FORMAT_LA' => 'format',
+	'NESTED_LA' => 'nested',
 	'NOT_LA' => 'not',
 	'NULLS_LA' => 'nulls',
 	'WITH_LA' => 'with',
diff --git a/src/interfaces/ecpg/preproc/parser.c b/src/interfaces/ecpg/preproc/parser.c
index 38e7acb680..47172fb780 100644
--- a/src/interfaces/ecpg/preproc/parser.c
+++ b/src/interfaces/ecpg/preproc/parser.c
@@ -79,6 +79,7 @@ filtered_base_yylex(void)
 	switch (cur_token)
 	{
 		case FORMAT:
+		case NESTED:
 		case NOT:
 		case NULLS_P:
 		case WITH:
@@ -122,6 +123,16 @@ filtered_base_yylex(void)
 			}
 			break;
 
+		case NESTED:
+			/* Replace NESTED by NESTED_LA if it's followed by PATH */
+			switch (next_token)
+			{
+				case PATH:
+					cur_token = NESTED_LA;
+					break;
+			}
+			break;
+
 		case NOT:
 			/* Replace NOT by NOT_LA if it's followed by BETWEEN, IN, etc */
 			switch (next_token)
-- 
2.35.3

