From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 19 Jun 2004 18:19:56 +0000 (+0000)
Subject: Fix oversight in recent rowtype-handling improvements: transformTargetList
X-Git-Tag: REL8_0_0BETA1~338
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f0cc132621cc9ee53054684112fca0ae3780b48d;p=postgresql

Fix oversight in recent rowtype-handling improvements: transformTargetList
should recognize 'foo.*' when the star appears in A_Indirection, not only
in ColumnRef.  This allows 'SELECT something.*' to do what the user
expects when the something is an expression yielding a row.
---

diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index d5d71b67af..883ad1ca9f 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.171 2004/06/16 01:26:45 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.172 2004/06/19 18:19:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,7 +38,6 @@ static Oid **argtype_inherit(int nargs, Oid *argtypes);
 
 static int	find_inheritors(Oid relid, Oid **supervec);
 static Oid **gen_cross_product(InhPaths *arginh, int nargs);
-static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
 static void unknown_attribute(ParseState *pstate, Node *relref, char *attname);
 
 
@@ -1131,33 +1130,6 @@ make_fn_arguments(ParseState *pstate,
 	}
 }
 
-/*
- * setup_field_select
- *		Build a FieldSelect node that says which attribute to project to.
- *		This routine is called by ParseFuncOrColumn() when we have found
- *		a projection on a function result or parameter.
- */
-static FieldSelect *
-setup_field_select(Node *input, char *attname, Oid relid)
-{
-	FieldSelect *fselect = makeNode(FieldSelect);
-	AttrNumber	attno;
-
-	attno = get_attnum(relid, attname);
-	if (attno == InvalidAttrNumber)
-		ereport(ERROR,
-				(errcode(ERRCODE_UNDEFINED_COLUMN),
-			 errmsg("column \"%s\" of relation \"%s\" does not exist",
-					attname, get_rel_name(relid))));
-
-	fselect->arg = (Expr *) input;
-	fselect->fieldnum = attno;
-	fselect->resulttype = get_atttype(relid, attno);
-	fselect->resulttypmod = get_atttypmod(relid, attno);
-
-	return fselect;
-}
-
 /*
  * ParseComplexProjection -
  *	  handles function calls with a single argument that is of complex type.
@@ -1170,6 +1142,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
 	Oid			argtype;
 	Oid			argrelid;
 	AttrNumber	attnum;
+	FieldSelect *fselect;
 
 	/*
 	 * Special case for whole-row Vars so that we can resolve (foo.*).bar
@@ -1205,7 +1178,14 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
 		return NULL;			/* funcname does not match any column */
 
 	/* Success, so generate a FieldSelect expression */
-	return (Node *) setup_field_select(first_arg, funcname, argrelid);
+	fselect = makeNode(FieldSelect);
+	fselect->arg = (Expr *) first_arg;
+	fselect->fieldnum = attnum;
+	get_atttypetypmod(argrelid, attnum,
+					  &fselect->resulttype,
+					  &fselect->resulttypmod);
+
+	return (Node *) fselect;
 }
 
 /*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index e0f0e6c930..7dcbc7b7b7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.121 2004/06/09 19:08:17 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.122 2004/06/19 18:19:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,7 @@
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
 
 
 static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
@@ -37,7 +38,9 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
 							   int32 targetTypMod,
 							   ListCell *indirection,
 							   Node *rhs);
+static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref);
 static List *ExpandAllTables(ParseState *pstate);
+static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind);
 static char *FigureColname(Node *node);
 static int	FigureColnameInternal(Node *node, char **name);
 
@@ -108,103 +111,47 @@ transformTargetList(ParseState *pstate, List *targetlist)
 	{
 		ResTarget  *res = (ResTarget *) lfirst(o_target);
 
+		/*
+		 * Check for "something.*".  Depending on the complexity of the
+		 * "something", the star could appear as the last name in ColumnRef,
+		 * or as the last indirection item in A_Indirection.
+		 */
 		if (IsA(res->val, ColumnRef))
 		{
 			ColumnRef  *cref = (ColumnRef *) res->val;
-			List	   *fields = cref->fields;
 
-			if (strcmp(strVal(llast(fields)), "*") == 0)
-			{
-				int		numnames = list_length(fields);
-
-				if (numnames == 1)
-				{
-					/*
-					 * Target item is a single '*', expand all tables
-					 * (e.g., SELECT * FROM emp)
-					 */
-					p_target = list_concat(p_target,
-										   ExpandAllTables(pstate));
-				}
-				else
-				{
-					/*
-					 * Target item is relation.*, expand that table
-					 * (e.g., SELECT emp.*, dname FROM emp, dept)
-					 */
-					char	   *schemaname;
-					char	   *relname;
-					RangeTblEntry *rte;
-					int			sublevels_up;
-
-					switch (numnames)
-					{
-						case 2:
-							schemaname = NULL;
-							relname = strVal(linitial(fields));
-							break;
-						case 3:
-							schemaname = strVal(linitial(fields));
-							relname = strVal(lsecond(fields));
-							break;
-						case 4:
-						{
-							char	   *name1 = strVal(linitial(fields));
-
-							/*
-							 * We check the catalog name and then ignore
-							 * it.
-							 */
-							if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
-								ereport(ERROR,
-										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-										 errmsg("cross-database references are not implemented: %s",
-												NameListToString(fields))));
-							schemaname = strVal(lsecond(fields));
-							relname = strVal(lthird(fields));
-							break;
-						}
-						default:
-							ereport(ERROR,
-									(errcode(ERRCODE_SYNTAX_ERROR),
-									 errmsg("improper qualified name (too many dotted names): %s",
-											NameListToString(fields))));
-							schemaname = NULL;		/* keep compiler quiet */
-							relname = NULL;
-							break;
-					}
-
-					rte = refnameRangeTblEntry(pstate, schemaname, relname,
-											   &sublevels_up);
-					if (rte == NULL)
-						rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
-																  relname));
-
-					p_target = list_concat(p_target,
-										   expandRelAttrs(pstate, rte));
-				}
-			}
-			else
+			if (strcmp(strVal(llast(cref->fields)), "*") == 0)
 			{
-				/* Plain ColumnRef node, treat it as an expression */
-				p_target = lappend(p_target,
-								   transformTargetEntry(pstate,
-														res->val,
-														NULL,
-														res->name,
-														false));
+				/* It is something.*, expand into multiple items */
+				p_target = list_concat(p_target,
+									   ExpandColumnRefStar(pstate, cref));
+				continue;
 			}
 		}
-		else
+		else if (IsA(res->val, A_Indirection))
 		{
-			/* Everything else but ColumnRef */
-			p_target = lappend(p_target,
-							   transformTargetEntry(pstate,
-													res->val,
-													NULL,
-													res->name,
-													false));
+			A_Indirection  *ind = (A_Indirection *) res->val;
+			Node	*lastitem = llast(ind->indirection);
+
+			if (IsA(lastitem, String) &&
+				strcmp(strVal(lastitem), "*") == 0)
+			{
+				/* It is something.*, expand into multiple items */
+				p_target = list_concat(p_target,
+									   ExpandIndirectionStar(pstate, ind));
+				continue;
+			}
 		}
+
+		/*
+		 * Not "something.*", so transform as a single expression
+		 */
+		p_target = lappend(p_target,
+						   transformTargetEntry(pstate,
+												res->val,
+												NULL,
+												res->name,
+												false));
 	}
 
 	return p_target;
@@ -719,8 +666,90 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
 	return cols;
 }
 
-/* ExpandAllTables()
- * Turns '*' (in the target list) into a list of targetlist entries.
+/*
+ * ExpandColumnRefStar()
+ *		Turns foo.* (in the target list) into a list of targetlist entries.
+ *
+ * This handles the case where '*' appears as the last or only name in a
+ * ColumnRef.
+ */
+static List *
+ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
+{
+	List	   *fields = cref->fields;
+	int			numnames = list_length(fields);
+
+	if (numnames == 1)
+	{
+		/*
+		 * Target item is a bare '*', expand all tables
+		 *
+		 * (e.g., SELECT * FROM emp, dept)
+		 */
+		return ExpandAllTables(pstate);
+	}
+	else
+	{
+		/*
+		 * Target item is relation.*, expand that table
+		 *
+		 * (e.g., SELECT emp.*, dname FROM emp, dept)
+		 */
+		char	   *schemaname;
+		char	   *relname;
+		RangeTblEntry *rte;
+		int			sublevels_up;
+
+		switch (numnames)
+		{
+			case 2:
+				schemaname = NULL;
+				relname = strVal(linitial(fields));
+				break;
+			case 3:
+				schemaname = strVal(linitial(fields));
+				relname = strVal(lsecond(fields));
+				break;
+			case 4:
+			{
+				char	   *name1 = strVal(linitial(fields));
+
+				/*
+				 * We check the catalog name and then ignore
+				 * it.
+				 */
+				if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
+					ereport(ERROR,
+							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							 errmsg("cross-database references are not implemented: %s",
+									NameListToString(fields))));
+				schemaname = strVal(lsecond(fields));
+				relname = strVal(lthird(fields));
+				break;
+			}
+			default:
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("improper qualified name (too many dotted names): %s",
+								NameListToString(fields))));
+				schemaname = NULL;		/* keep compiler quiet */
+				relname = NULL;
+				break;
+		}
+
+		rte = refnameRangeTblEntry(pstate, schemaname, relname,
+								   &sublevels_up);
+		if (rte == NULL)
+			rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
+													  relname));
+
+		return expandRelAttrs(pstate, rte);
+	}
+}
+
+/*
+ * ExpandAllTables()
+ *		Turns '*' (in the target list) into a list of targetlist entries.
  *
  * tlist entries are generated for each relation appearing at the top level
  * of the query's namespace, except for RTEs marked not inFromCl.  (These
@@ -770,6 +799,84 @@ ExpandAllTables(ParseState *pstate)
 	return target;
 }
 
+/*
+ * ExpandIndirectionStar()
+ *		Turns foo.* (in the target list) into a list of targetlist entries.
+ *
+ * This handles the case where '*' appears as the last item in A_Indirection.
+ */
+static List *
+ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind)
+{
+	Node	   *expr;
+	TupleDesc	tupleDesc;
+	int			numAttrs;
+	int			i;
+	List	   *te_list = NIL;
+
+	/* 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);
+
+	/* And transform that */
+	expr = transformExpr(pstate, (Node *) ind);
+
+	/* Verify it's a composite type, and get the tupdesc */
+	tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
+
+	/* Generate a list of references to the individual fields */
+	numAttrs = tupleDesc->natts;
+	for (i = 0; i < numAttrs; i++)
+	{
+		Form_pg_attribute att = tupleDesc->attrs[i];
+		Node	   *fieldnode;
+		TargetEntry *te;
+
+		if (att->attisdropped)
+			continue;
+
+		/*
+		 * If we got a whole-row Var from the rowtype reference, we can
+		 * expand the fields as simple Vars.  Otherwise we must generate
+		 * multiple copies of the rowtype reference and do FieldSelects.
+		 */
+		if (IsA(expr, Var) &&
+			((Var *) expr)->varattno == InvalidAttrNumber)
+		{
+			Var		   *var = (Var *) expr;
+
+			fieldnode = (Node *) makeVar(var->varno,
+										 i + 1,
+										 att->atttypid,
+										 att->atttypmod,
+										 var->varlevelsup);
+		}
+		else
+		{
+			FieldSelect *fselect = makeNode(FieldSelect);
+
+			fselect->arg = (Expr *) copyObject(expr);
+			fselect->fieldnum = i + 1;
+			fselect->resulttype = att->atttypid;
+			fselect->resulttypmod = att->atttypmod;
+
+			fieldnode = (Node *) fselect;
+		}
+
+		te = makeNode(TargetEntry);
+		te->resdom = makeResdom((AttrNumber) pstate->p_next_resno++,
+								att->atttypid,
+								att->atttypmod,
+								pstrdup(NameStr(att->attname)),
+								false);
+		te->expr = (Expr *) fieldnode;
+		te_list = lappend(te_list, te);
+	}
+
+	return te_list;
+}
+
 /*
  * FigureColname -
  *	  if the name of the resulting column is not specified in the target
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 8ad60423c7..362480b250 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -36,7 +36,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.7 2004/06/05 01:55:05 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.8 2004/06/19 18:19:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -409,8 +409,8 @@ lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
 		if (typentry->tupDesc == NULL && !noError)
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("type %u is not composite",
-							type_id)));
+					 errmsg("type %s is not composite",
+							format_type_be(type_id))));
 		return typentry->tupDesc;
 	}
 	else