Cause view/rule display to work as expected after rename of an underlying
authorTom Lane
Thu, 8 Aug 2002 17:00:19 +0000 (17:00 +0000)
committerTom Lane
Thu, 8 Aug 2002 17:00:19 +0000 (17:00 +0000)
table or column, or of an output column of the view itself.

src/backend/parser/parse_relation.c
src/backend/utils/adt/ruleutils.c

index 229eab829a533a113562d0bd65bbcad06bac562e..6713c665098dac020ad90e1bdf4fa8c13c920123 100644 (file)
@@ -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 (attnum > 0 && attnum <= length(rte->eref->colnames))
-       return strVal(nth(attnum - 1, rte->eref->colnames));
+   if (rte->alias &&
+       attnum > 0 && attnum <= length(rte->alias->colnames))
+       return strVal(nth(attnum - 1, rte->alias->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 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)
-       elog(ERROR, "Invalid attnum %d for rangetable entry %s",
-            attnum, rte->eref->aliasname);
+   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;
+   }
 
    /*
-    * Use the real name of the table's column
+    * Otherwise use the column name from eref.  There should always be one.
     */
-   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;
+   if (attnum > 0 && attnum <= length(rte->eref->colnames))
+       return strVal(nth(attnum - 1, rte->eref->colnames));
+
+   elog(ERROR, "Invalid attnum %d for rangetable entry %s",
+        attnum, rte->eref->aliasname);
+   return NULL;                /* keep compiler quiet */
 }
 
 /*
index 98bbba534aa742a2e2d6c97b7a0737a99e945ce8..302f9a75ce77ba9490c87be6e36e31a7179a07db 100644 (file)
@@ -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))
    {