From 80ab3da1c748dd1abe9fd980d8de1862407d861d Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Sat, 1 Apr 2023 23:15:26 +0300
Subject: [PATCH v6 05/11] Allow processing of .* by generic subscripting

---
 src/backend/parser/gram.y         |  2 ++
 src/backend/parser/parse_expr.c   | 34 ++++++++++++------
 src/backend/parser/parse_target.c | 60 ++++++++++++++++++++-----------
 src/backend/utils/adt/ruleutils.c |  6 +++-
 src/include/parser/parse_expr.h   |  3 ++
 5 files changed, 73 insertions(+), 32 deletions(-)

diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 67eb96396af..a8ee79518ca 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -18889,6 +18889,7 @@ check_func_name(List *names, core_yyscan_t yyscanner)
 static List *
 check_indirection(List *indirection, core_yyscan_t yyscanner)
 {
+#if 0
 	ListCell *l;
 
 	foreach(l, indirection)
@@ -18899,6 +18900,7 @@ check_indirection(List *indirection, core_yyscan_t yyscanner)
 				parser_yyerror("improper use of \"*\"");
 		}
 	}
+#endif
 	return indirection;
 }
 
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 735d5c88729..3ed01864f66 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -75,7 +75,6 @@ static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
 static Node *transformWholeRowRef(ParseState *pstate,
 								  ParseNamespaceItem *nsitem,
 								  int sublevels_up, int location);
-static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
 static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
 static Node *transformJsonObjectConstructor(ParseState *pstate,
@@ -159,7 +158,7 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 			break;
 
 		case T_A_Indirection:
-			result = transformIndirection(pstate, (A_Indirection *) expr);
+			result = transformIndirection(pstate, (A_Indirection *) expr, NULL);
 			break;
 
 		case T_A_ArrayExpr:
@@ -433,8 +432,9 @@ unknown_attribute(ParseState *pstate, Node *relref, const char *attname,
 	}
 }
 
-static Node *
-transformIndirection(ParseState *pstate, A_Indirection *ind)
+Node *
+transformIndirection(ParseState *pstate, A_Indirection *ind,
+					 bool *trailing_star_expansion)
 {
 	Node	   *last_srf = pstate->p_last_srf;
 	Node	   *result = transformExprRecurse(pstate, ind->arg);
@@ -454,12 +454,7 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
 		if (IsA(n, A_Indices))
 			subscripts = lappend(subscripts, n);
 		else if (IsA(n, A_Star))
-		{
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("row expansion via \"*\" is not supported here"),
-					 parser_errposition(pstate, location)));
-		}
+			subscripts = lappend(subscripts, n);
 		else
 		{
 			Assert(IsA(n, String));
@@ -526,7 +521,21 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
 
 			n = linitial(subscripts);
 
-			if (!IsA(n, String))
+			if (IsA(n, A_Star))
+			{
+				/* Success, if trailing star expansion is allowed */
+				if (trailing_star_expansion && list_length(subscripts) == 1)
+				{
+					*trailing_star_expansion = true;
+					return result;
+				}
+
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("row expansion via \"*\" is not supported here"),
+						 parser_errposition(pstate, location)));
+			}
+			else if (!IsA(n, String))
 				ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("cannot subscript type %s because it does not support subscripting",
@@ -552,6 +561,9 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
 		result = newresult;
 	}
 
+	if (trailing_star_expansion)
+		*trailing_star_expansion = false;
+
 	return result;
 }
 
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 2dbcfd7488b..9041e424e1a 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -48,7 +48,7 @@ static Node *transformAssignmentSubscripts(ParseState *pstate,
 static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
 								 bool make_target_entry);
 static List *ExpandAllTables(ParseState *pstate, int location);
-static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
+static Node *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
 								   bool make_target_entry, ParseExprKind exprKind);
 static List *ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem,
 							   int sublevels_up, int location,
@@ -134,6 +134,7 @@ transformTargetList(ParseState *pstate, List *targetlist,
 	foreach(o_target, targetlist)
 	{
 		ResTarget  *res = (ResTarget *) lfirst(o_target);
+		Node	   *transformed = NULL;
 
 		/*
 		 * Check for "something.*".  Depending on the complexity of the
@@ -162,13 +163,19 @@ transformTargetList(ParseState *pstate, List *targetlist,
 
 				if (IsA(llast(ind->indirection), A_Star))
 				{
-					/* It is something.*, expand into multiple items */
-					p_target = list_concat(p_target,
-										   ExpandIndirectionStar(pstate,
-																 ind,
-																 true,
-																 exprKind));
-					continue;
+					Node	   *columns = ExpandIndirectionStar(pstate,
+																ind,
+																true,
+																exprKind);
+
+					if (IsA(columns, List))
+					{
+						/* It is something.*, expand into multiple items */
+						p_target = list_concat(p_target, (List *) columns);
+						continue;
+					}
+
+					transformed = (Node *) columns;
 				}
 			}
 		}
@@ -180,7 +187,7 @@ transformTargetList(ParseState *pstate, List *targetlist,
 		p_target = lappend(p_target,
 						   transformTargetEntry(pstate,
 												res->val,
-												NULL,
+												transformed,
 												exprKind,
 												res->name,
 												false));
@@ -251,10 +258,15 @@ transformExpressionList(ParseState *pstate, List *exprlist,
 
 			if (IsA(llast(ind->indirection), A_Star))
 			{
-				/* It is something.*, expand into multiple items */
-				result = list_concat(result,
-									 ExpandIndirectionStar(pstate, ind,
-														   false, exprKind));
+				Node	   *cols = ExpandIndirectionStar(pstate, ind,
+														 false, exprKind);
+
+				if (!cols || IsA(cols, List))
+					/* It is something.*, expand into multiple items */
+					result = list_concat(result, (List *) cols);
+				else
+					result = lappend(result, cols);
+
 				continue;
 			}
 		}
@@ -1348,22 +1360,30 @@ ExpandAllTables(ParseState *pstate, int location)
  * For robustness, we use a separate "make_target_entry" flag to control
  * this rather than relying on exprKind.
  */
-static List *
+static Node *
 ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
 					  bool make_target_entry, ParseExprKind exprKind)
 {
 	Node	   *expr;
+	ParseExprKind sv_expr_kind;
+	bool		trailing_star_expansion = false;
+
+	/* Save and restore identity of expression type we're parsing */
+	Assert(exprKind != EXPR_KIND_NONE);
+	sv_expr_kind = pstate->p_expr_kind;
+	pstate->p_expr_kind = exprKind;
 
 	/* Strip off the '*' to create a reference to the rowtype object */
-	ind = copyObject(ind);
-	ind->indirection = list_truncate(ind->indirection,
-									 list_length(ind->indirection) - 1);
+	expr = transformIndirection(pstate, ind, &trailing_star_expansion);
+
+	pstate->p_expr_kind = sv_expr_kind;
 
-	/* And transform that */
-	expr = transformExpr(pstate, (Node *) ind, exprKind);
+	/* '*' was consumed by generic type subscripting */
+	if (!trailing_star_expansion)
+		return expr;
 
 	/* Expand the rowtype expression into individual fields */
-	return ExpandRowReference(pstate, expr, make_target_entry);
+	return (Node *) ExpandRowReference(pstate, expr, make_target_entry);
 }
 
 /*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index b0b25d8b9e5..fd92ac663d4 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -12841,7 +12841,11 @@ printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
 	{
 		Node	   *up = (Node *) lfirst(uplist_item);
 
-		if (IsA(up, String))
+		if (!up)
+		{
+			appendStringInfoString(buf, ".*");
+		}
+		else if (IsA(up, String))
 		{
 			appendStringInfoChar(buf, '.');
 			appendStringInfoString(buf, quote_identifier(strVal(up)));
diff --git a/src/include/parser/parse_expr.h b/src/include/parser/parse_expr.h
index f1f479fa56b..7f1889e9d84 100644
--- a/src/include/parser/parse_expr.h
+++ b/src/include/parser/parse_expr.h
@@ -23,4 +23,7 @@ extern Node *transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKin
 
 extern const char *ParseExprKindName(ParseExprKind exprKind);
 
+extern Node *transformIndirection(ParseState *pstate, A_Indirection *ind,
+								  bool *trailing_star_expansion);
+
 #endif							/* PARSE_EXPR_H */
-- 
2.25.1

