From 9459db8ea9c84444f53495c09f2908b353ff95ea Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 8 Aug 2002 17:00:19 +0000
Subject: [PATCH] Cause view/rule display to work as expected after rename of
 an underlying table or column, or of an output column of the view itself.

---
 src/backend/parser/parse_relation.c |  47 +++++++-----
 src/backend/utils/adt/ruleutils.c   | 115 ++++++++++++++++++----------
 2 files changed, 103 insertions(+), 59 deletions(-)

diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 229eab829a..6713c66509 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.76 2002/08/08 01:44:30 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.77 2002/08/08 17:00:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1499,29 +1499,36 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
 		return "*";
 
 	/*
-	 * If there is an alias, use it.  (This path should always be taken
-	 * for non-relation RTEs.)
+	 * If there is a user-written column alias, use it.
+	 */
+	if (rte->alias &&
+		attnum > 0 && attnum <= length(rte->alias->colnames))
+		return strVal(nth(attnum - 1, rte->alias->colnames));
+
+	/*
+	 * If the RTE is a relation, go to the system catalogs not the
+	 * eref->colnames list.  This is a little slower but it will give
+	 * the right answer if the column has been renamed since the eref
+	 * list was built (which can easily happen for rules).
+	 */
+	if (rte->rtekind == RTE_RELATION)
+	{
+		attname = get_attname(rte->relid, attnum);
+		if (attname == NULL)
+			elog(ERROR, "cache lookup of attribute %d in relation %u failed",
+				 attnum, rte->relid);
+		return attname;
+	}
+
+	/*
+	 * Otherwise use the column name from eref.  There should always be one.
 	 */
 	if (attnum > 0 && attnum <= length(rte->eref->colnames))
 		return strVal(nth(attnum - 1, rte->eref->colnames));
 
-	/*
-	 * Can get here for a system attribute (which never has an alias), or
-	 * if alias name list is too short (which probably can't happen
-	 * anymore).  Neither of these cases is valid for a non-relation RTE.
-	 */
-	if (rte->rtekind != RTE_RELATION)
-		elog(ERROR, "Invalid attnum %d for rangetable entry %s",
-			 attnum, rte->eref->aliasname);
-
-	/*
-	 * Use the real name of the table's column
-	 */
-	attname = get_attname(rte->relid, attnum);
-	if (attname == NULL)
-		elog(ERROR, "cache lookup of attribute %d in relation %u failed",
-			 attnum, rte->relid);
-	return attname;
+	elog(ERROR, "Invalid attnum %d for rangetable entry %s",
+		 attnum, rte->eref->aliasname);
+	return NULL;				/* keep compiler quiet */
 }
 
 /*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 98bbba534a..302f9a75ce 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.113 2002/08/08 01:44:31 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.114 2002/08/08 17:00:19 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -118,15 +118,19 @@ static char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_c
 static text *pg_do_getviewdef(Oid viewoid);
 static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
-static void get_query_def(Query *query, StringInfo buf, List *parentnamespace);
-static void get_select_query_def(Query *query, deparse_context *context);
+static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
+						  TupleDesc resultDesc);
+static void get_select_query_def(Query *query, deparse_context *context,
+								 TupleDesc resultDesc);
 static void get_insert_query_def(Query *query, deparse_context *context);
 static void get_update_query_def(Query *query, deparse_context *context);
 static void get_delete_query_def(Query *query, deparse_context *context);
 static void get_utility_query_def(Query *query, deparse_context *context);
-static void get_basic_select_query(Query *query, deparse_context *context);
+static void get_basic_select_query(Query *query, deparse_context *context,
+								   TupleDesc resultDesc);
 static void get_setop_query(Node *setOp, Query *query,
-							deparse_context *context);
+							deparse_context *context,
+							TupleDesc resultDesc);
 static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
 						 bool force_colno,
 						 deparse_context *context);
@@ -936,25 +940,22 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
 		foreach(action, actions)
 		{
 			query = (Query *) lfirst(action);
-			get_query_def(query, buf, NIL);
+			get_query_def(query, buf, NIL, NULL);
 			appendStringInfo(buf, "; ");
 		}
 		appendStringInfo(buf, ");");
 	}
+	else if (length(actions) == 0)
+	{
+		appendStringInfo(buf, "NOTHING;");
+	}
 	else
 	{
-		if (length(actions) == 0)
-		{
-			appendStringInfo(buf, "NOTHING;");
-		}
-		else
-		{
-			Query	   *query;
+		Query	   *query;
 
-			query = (Query *) lfirst(actions);
-			get_query_def(query, buf, NIL);
-			appendStringInfo(buf, ";");
-		}
+		query = (Query *) lfirst(actions);
+		get_query_def(query, buf, NIL, NULL);
+		appendStringInfo(buf, ";");
 	}
 }
 
@@ -975,6 +976,7 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
 	char	   *ev_qual;
 	char	   *ev_action;
 	List	   *actions = NIL;
+	Relation	ev_relation;
 	int			fno;
 	bool		isnull;
 
@@ -1010,25 +1012,31 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
 	query = (Query *) lfirst(actions);
 
 	if (ev_type != '1' || ev_attr >= 0 || !is_instead ||
-		strcmp(ev_qual, "<>") != 0)
+		strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
 	{
 		appendStringInfo(buf, "Not a view");
 		return;
 	}
 
-	get_query_def(query, buf, NIL);
+	ev_relation = heap_open(ev_class, AccessShareLock);
+
+	get_query_def(query, buf, NIL, RelationGetDescr(ev_relation));
 	appendStringInfo(buf, ";");
+
+	heap_close(ev_relation, AccessShareLock);
 }
 
 
 /* ----------
- * get_query_def			- Parse back one action from
- *					  the parsetree in the actions
- *					  list
+ * get_query_def			- Parse back one query parsetree
+ *
+ * If resultDesc is not NULL, then it is the output tuple descriptor for
+ * the view represented by a SELECT query.
  * ----------
  */
 static void
-get_query_def(Query *query, StringInfo buf, List *parentnamespace)
+get_query_def(Query *query, StringInfo buf, List *parentnamespace,
+			  TupleDesc resultDesc)
 {
 	deparse_context context;
 	deparse_namespace dpns;
@@ -1044,7 +1052,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace)
 	switch (query->commandType)
 	{
 		case CMD_SELECT:
-			get_select_query_def(query, &context);
+			get_select_query_def(query, &context, resultDesc);
 			break;
 
 		case CMD_UPDATE:
@@ -1080,7 +1088,8 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace)
  * ----------
  */
 static void
-get_select_query_def(Query *query, deparse_context *context)
+get_select_query_def(Query *query, deparse_context *context,
+					 TupleDesc resultDesc)
 {
 	StringInfo	buf = context->buf;
 	bool		force_colno;
@@ -1094,13 +1103,13 @@ get_select_query_def(Query *query, deparse_context *context)
 	 */
 	if (query->setOperations)
 	{
-		get_setop_query(query->setOperations, query, context);
+		get_setop_query(query->setOperations, query, context, resultDesc);
 		/* ORDER BY clauses must be simple in this case */
 		force_colno = true;
 	}
 	else
 	{
-		get_basic_select_query(query, context);
+		get_basic_select_query(query, context, resultDesc);
 		force_colno = false;
 	}
 
@@ -1151,11 +1160,13 @@ get_select_query_def(Query *query, deparse_context *context)
 }
 
 static void
-get_basic_select_query(Query *query, deparse_context *context)
+get_basic_select_query(Query *query, deparse_context *context,
+					   TupleDesc resultDesc)
 {
 	StringInfo	buf = context->buf;
 	char	   *sep;
 	List	   *l;
+	int			colno;
 
 	/*
 	 * Build up the query string - first we say SELECT
@@ -1186,23 +1197,37 @@ get_basic_select_query(Query *query, deparse_context *context)
 
 	/* Then we tell what to select (the targetlist) */
 	sep = " ";
+	colno = 0;
 	foreach(l, query->targetList)
 	{
 		TargetEntry *tle = (TargetEntry *) lfirst(l);
 		bool		tell_as = false;
+		char	   *colname;
 
 		if (tle->resdom->resjunk)
 			continue;			/* ignore junk entries */
 
 		appendStringInfo(buf, sep);
 		sep = ", ";
+		colno++;
 
 		/* Do NOT use get_tle_expr here; see its comments! */
 		get_rule_expr(tle->expr, context);
 
+		/*
+		 * Figure out what the result column should be called.  In the
+		 * context of a view, use the view's tuple descriptor (so as to
+		 * pick up the effects of any column RENAME that's been done on the
+		 * view).  Otherwise, just use what we can find in the TLE.
+		 */
+		if (resultDesc && colno <= resultDesc->natts)
+			colname = NameStr(resultDesc->attrs[colno-1]->attname);
+		else
+			colname = tle->resdom->resname;
+
 		/* Check if we must say AS ... */
 		if (!IsA(tle->expr, Var))
-			tell_as = (strcmp(tle->resdom->resname, "?column?") != 0);
+			tell_as = (strcmp(colname, "?column?") != 0);
 		else
 		{
 			Var		   *var = (Var *) (tle->expr);
@@ -1212,13 +1237,12 @@ get_basic_select_query(Query *query, deparse_context *context)
 
 			get_names_for_var(var, context, &schemaname, &refname, &attname);
 			tell_as = (attname == NULL ||
-					   strcmp(attname, tle->resdom->resname) != 0);
+					   strcmp(attname, colname) != 0);
 		}
 
 		/* and do if so */
 		if (tell_as)
-			appendStringInfo(buf, " AS %s",
-							 quote_identifier(tle->resdom->resname));
+			appendStringInfo(buf, " AS %s", quote_identifier(colname));
 	}
 
 	/* Add the FROM clause if needed */
@@ -1256,7 +1280,8 @@ get_basic_select_query(Query *query, deparse_context *context)
 }
 
 static void
-get_setop_query(Node *setOp, Query *query, deparse_context *context)
+get_setop_query(Node *setOp, Query *query, deparse_context *context,
+				TupleDesc resultDesc)
 {
 	StringInfo	buf = context->buf;
 
@@ -1267,14 +1292,14 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context)
 		Query	   *subquery = rte->subquery;
 
 		Assert(subquery != NULL);
-		get_query_def(subquery, buf, context->namespaces);
+		get_query_def(subquery, buf, context->namespaces, resultDesc);
 	}
 	else if (IsA(setOp, SetOperationStmt))
 	{
 		SetOperationStmt *op = (SetOperationStmt *) setOp;
 
 		appendStringInfo(buf, "((");
-		get_setop_query(op->larg, query, context);
+		get_setop_query(op->larg, query, context, resultDesc);
 		switch (op->op)
 		{
 			case SETOP_UNION:
@@ -1294,7 +1319,7 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context)
 			appendStringInfo(buf, "ALL (");
 		else
 			appendStringInfo(buf, "(");
-		get_setop_query(op->rarg, query, context);
+		get_setop_query(op->rarg, query, context, resultDesc);
 		appendStringInfo(buf, "))");
 	}
 	else
@@ -1405,7 +1430,7 @@ get_insert_query_def(Query *query, deparse_context *context)
 		appendStringInfoChar(buf, ')');
 	}
 	else
-		get_query_def(select_rte->subquery, buf, NIL);
+		get_query_def(select_rte->subquery, buf, NIL, NULL);
 }
 
 
@@ -2418,7 +2443,7 @@ get_sublink_expr(Node *node, deparse_context *context)
 	if (need_paren)
 		appendStringInfoChar(buf, '(');
 
-	get_query_def(query, buf, context->namespaces);
+	get_query_def(query, buf, context->namespaces, NULL);
 
 	if (need_paren)
 		appendStringInfo(buf, "))");
@@ -2491,7 +2516,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 			case RTE_SUBQUERY:
 				/* Subquery RTE */
 				appendStringInfoChar(buf, '(');
-				get_query_def(rte->subquery, buf, context->namespaces);
+				get_query_def(rte->subquery, buf, context->namespaces, NULL);
 				appendStringInfoChar(buf, ')');
 				break;
 			case RTE_FUNCTION:
@@ -2521,6 +2546,18 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 				appendStringInfoChar(buf, ')');
 			}
 		}
+		else if (rte->rtekind == RTE_RELATION &&
+				 strcmp(rte->eref->aliasname, get_rel_name(rte->relid)) != 0)
+		{
+			/*
+			 * Apparently the rel has been renamed since the rule was made.
+			 * Emit a fake alias clause so that variable references will
+			 * still work.  This is not a 100% solution but should work in
+			 * most reasonable situations.
+			 */
+			appendStringInfo(buf, " %s",
+							 quote_identifier(rte->eref->aliasname));
+		}
 	}
 	else if (IsA(jtnode, JoinExpr))
 	{