From face4d14fb335204d024456e63b6dca7bdc1f97d Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Sun, 2 Apr 2023 00:13:38 +0300
Subject: [PATCH v6 08/11] Extract transformColumnRefInternal()

---
 src/backend/parser/parse_expr.c | 238 ++++++++++++++++++--------------
 1 file changed, 132 insertions(+), 106 deletions(-)

diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 3ed01864f66..b82d25c40a4 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -567,116 +567,31 @@ transformIndirection(ParseState *pstate, A_Indirection *ind,
 	return result;
 }
 
-/*
- * Transform a ColumnRef.
- *
- * If you find yourself changing this code, see also ExpandColumnRefStar.
- */
-static Node *
-transformColumnRef(ParseState *pstate, ColumnRef *cref)
+typedef struct ColRefError
 {
-	Node	   *node = NULL;
-	char	   *nspname = NULL;
-	char	   *relname = NULL;
-	char	   *colname = NULL;
-	ParseNamespaceItem *nsitem;
-	int			levels_up;
 	enum
 	{
 		CRERR_NO_COLUMN,
 		CRERR_NO_RTE,
 		CRERR_WRONG_DB,
 		CRERR_TOO_MANY
-	}			crerr = CRERR_NO_COLUMN;
-	const char *err;
+	}			code;
+	char	   *nspname;
+	char	   *relname;
+	char	   *colname;
+} ColRefError;
 
-	/*
-	 * Check to see if the column reference is in an invalid place within the
-	 * query.  We allow column references in most places, except in default
-	 * expressions and partition bound expressions.
-	 */
-	err = NULL;
-	switch (pstate->p_expr_kind)
-	{
-		case EXPR_KIND_NONE:
-			Assert(false);		/* can't happen */
-			break;
-		case EXPR_KIND_OTHER:
-		case EXPR_KIND_JOIN_ON:
-		case EXPR_KIND_JOIN_USING:
-		case EXPR_KIND_FROM_SUBSELECT:
-		case EXPR_KIND_FROM_FUNCTION:
-		case EXPR_KIND_WHERE:
-		case EXPR_KIND_POLICY:
-		case EXPR_KIND_HAVING:
-		case EXPR_KIND_FILTER:
-		case EXPR_KIND_WINDOW_PARTITION:
-		case EXPR_KIND_WINDOW_ORDER:
-		case EXPR_KIND_WINDOW_FRAME_RANGE:
-		case EXPR_KIND_WINDOW_FRAME_ROWS:
-		case EXPR_KIND_WINDOW_FRAME_GROUPS:
-		case EXPR_KIND_SELECT_TARGET:
-		case EXPR_KIND_INSERT_TARGET:
-		case EXPR_KIND_UPDATE_SOURCE:
-		case EXPR_KIND_UPDATE_TARGET:
-		case EXPR_KIND_MERGE_WHEN:
-		case EXPR_KIND_GROUP_BY:
-		case EXPR_KIND_ORDER_BY:
-		case EXPR_KIND_DISTINCT_ON:
-		case EXPR_KIND_LIMIT:
-		case EXPR_KIND_OFFSET:
-		case EXPR_KIND_RETURNING:
-		case EXPR_KIND_MERGE_RETURNING:
-		case EXPR_KIND_VALUES:
-		case EXPR_KIND_VALUES_SINGLE:
-		case EXPR_KIND_CHECK_CONSTRAINT:
-		case EXPR_KIND_DOMAIN_CHECK:
-		case EXPR_KIND_FUNCTION_DEFAULT:
-		case EXPR_KIND_INDEX_EXPRESSION:
-		case EXPR_KIND_INDEX_PREDICATE:
-		case EXPR_KIND_STATS_EXPRESSION:
-		case EXPR_KIND_ALTER_COL_TRANSFORM:
-		case EXPR_KIND_EXECUTE_PARAMETER:
-		case EXPR_KIND_TRIGGER_WHEN:
-		case EXPR_KIND_PARTITION_EXPRESSION:
-		case EXPR_KIND_CALL_ARGUMENT:
-		case EXPR_KIND_COPY_WHERE:
-		case EXPR_KIND_GENERATED_COLUMN:
-		case EXPR_KIND_CYCLE_MARK:
-			/* okay */
-			break;
-
-		case EXPR_KIND_COLUMN_DEFAULT:
-			err = _("cannot use column reference in DEFAULT expression");
-			break;
-		case EXPR_KIND_PARTITION_BOUND:
-			err = _("cannot use column reference in partition bound expression");
-			break;
-
-			/*
-			 * There is intentionally no default: case here, so that the
-			 * compiler will warn if we add a new ParseExprKind without
-			 * extending this switch.  If we do see an unrecognized value at
-			 * runtime, the behavior will be the same as for EXPR_KIND_OTHER,
-			 * which is sane anyway.
-			 */
-	}
-	if (err)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg_internal("%s", err),
-				 parser_errposition(pstate, cref->location)));
-
-	/*
-	 * Give the PreParseColumnRefHook, if any, first shot.  If it returns
-	 * non-null then that's all, folks.
-	 */
-	if (pstate->p_pre_columnref_hook != NULL)
-	{
-		node = pstate->p_pre_columnref_hook(pstate, cref);
-		if (node != NULL)
-			return node;
-	}
+static Node *
+transformColumnRefInternal(ParseState *pstate, ColumnRef *cref, int nfields,
+						   ColRefError *err)
+{
+	ParseNamespaceItem *nsitem;
+	Node	   *node = NULL;
+	char	   *nspname = NULL;
+	char	   *relname = NULL;
+	char	   *colname = NULL;
+	int			crerr = CRERR_NO_COLUMN;
+	int			levels_up;
 
 	/*----------
 	 * The allowed syntaxes are:
@@ -701,7 +616,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 	 * database name; we check it here and then discard it.
 	 *----------
 	 */
-	switch (list_length(cref->fields))
+	switch (nfields)
 	{
 		case 1:
 			{
@@ -890,6 +805,117 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 			break;
 	}
 
+	err->code = crerr;
+	err->nspname = nspname;
+	err->relname = relname;
+	err->colname = colname;
+
+	return node;
+}
+
+/*
+ * Transform a ColumnRef.
+ *
+ * If you find yourself changing this code, see also ExpandColumnRefStar.
+ */
+static Node *
+transformColumnRef(ParseState *pstate, ColumnRef *cref)
+{
+	Node	   *node = NULL;
+	ColRefError crerr;
+	const char *err;
+
+	/*
+	 * Check to see if the column reference is in an invalid place within the
+	 * query.  We allow column references in most places, except in default
+	 * expressions and partition bound expressions.
+	 */
+	err = NULL;
+	switch (pstate->p_expr_kind)
+	{
+		case EXPR_KIND_NONE:
+			Assert(false);		/* can't happen */
+			break;
+		case EXPR_KIND_OTHER:
+		case EXPR_KIND_JOIN_ON:
+		case EXPR_KIND_JOIN_USING:
+		case EXPR_KIND_FROM_SUBSELECT:
+		case EXPR_KIND_FROM_FUNCTION:
+		case EXPR_KIND_WHERE:
+		case EXPR_KIND_POLICY:
+		case EXPR_KIND_HAVING:
+		case EXPR_KIND_FILTER:
+		case EXPR_KIND_WINDOW_PARTITION:
+		case EXPR_KIND_WINDOW_ORDER:
+		case EXPR_KIND_WINDOW_FRAME_RANGE:
+		case EXPR_KIND_WINDOW_FRAME_ROWS:
+		case EXPR_KIND_WINDOW_FRAME_GROUPS:
+		case EXPR_KIND_SELECT_TARGET:
+		case EXPR_KIND_INSERT_TARGET:
+		case EXPR_KIND_UPDATE_SOURCE:
+		case EXPR_KIND_UPDATE_TARGET:
+		case EXPR_KIND_MERGE_WHEN:
+		case EXPR_KIND_GROUP_BY:
+		case EXPR_KIND_ORDER_BY:
+		case EXPR_KIND_DISTINCT_ON:
+		case EXPR_KIND_LIMIT:
+		case EXPR_KIND_OFFSET:
+		case EXPR_KIND_RETURNING:
+		case EXPR_KIND_MERGE_RETURNING:
+		case EXPR_KIND_VALUES:
+		case EXPR_KIND_VALUES_SINGLE:
+		case EXPR_KIND_CHECK_CONSTRAINT:
+		case EXPR_KIND_DOMAIN_CHECK:
+		case EXPR_KIND_FUNCTION_DEFAULT:
+		case EXPR_KIND_INDEX_EXPRESSION:
+		case EXPR_KIND_INDEX_PREDICATE:
+		case EXPR_KIND_STATS_EXPRESSION:
+		case EXPR_KIND_ALTER_COL_TRANSFORM:
+		case EXPR_KIND_EXECUTE_PARAMETER:
+		case EXPR_KIND_TRIGGER_WHEN:
+		case EXPR_KIND_PARTITION_EXPRESSION:
+		case EXPR_KIND_CALL_ARGUMENT:
+		case EXPR_KIND_COPY_WHERE:
+		case EXPR_KIND_GENERATED_COLUMN:
+		case EXPR_KIND_CYCLE_MARK:
+			/* okay */
+			break;
+
+		case EXPR_KIND_COLUMN_DEFAULT:
+			err = _("cannot use column reference in DEFAULT expression");
+			break;
+		case EXPR_KIND_PARTITION_BOUND:
+			err = _("cannot use column reference in partition bound expression");
+			break;
+
+			/*
+			 * There is intentionally no default: case here, so that the
+			 * compiler will warn if we add a new ParseExprKind without
+			 * extending this switch.  If we do see an unrecognized value at
+			 * runtime, the behavior will be the same as for EXPR_KIND_OTHER,
+			 * which is sane anyway.
+			 */
+	}
+	if (err)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg_internal("%s", err),
+				 parser_errposition(pstate, cref->location)));
+
+	/*
+	 * Give the PreParseColumnRefHook, if any, first shot.  If it returns
+	 * non-null then that's all, folks.
+	 */
+	if (pstate->p_pre_columnref_hook != NULL)
+	{
+		node = pstate->p_pre_columnref_hook(pstate, cref);
+		if (node != NULL)
+			return node;
+	}
+
+	node = transformColumnRefInternal(pstate, cref, list_length(cref->fields),
+									  &crerr);
+
 	/*
 	 * Now give the PostParseColumnRefHook, if any, a chance.  We pass the
 	 * translation-so-far so that it can throw an error if it wishes in the
@@ -919,13 +945,13 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 	 */
 	if (node == NULL)
 	{
-		switch (crerr)
+		switch (crerr.code)
 		{
 			case CRERR_NO_COLUMN:
-				errorMissingColumn(pstate, relname, colname, cref->location);
+				errorMissingColumn(pstate, crerr.relname, crerr.colname, cref->location);
 				break;
 			case CRERR_NO_RTE:
-				errorMissingRTE(pstate, makeRangeVar(nspname, relname,
+				errorMissingRTE(pstate, makeRangeVar(crerr.nspname, crerr.relname,
 													 cref->location));
 				break;
 			case CRERR_WRONG_DB:
-- 
2.25.1

