]> granicus.if.org Git - postgresql/commitdiff
Revise handling of dropped columns in JOIN alias lists to avoid a
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 3 Jun 2005 23:05:30 +0000 (23:05 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 3 Jun 2005 23:05:30 +0000 (23:05 +0000)
performance problem pointed out by phil@vodafone: to wit, we were
spending O(N^2) time to check dropped-ness in an N-deep join tree,
even in the case where the tree was freshly constructed and couldn't
possibly mention any dropped columns.  Instead of recursing in
get_rte_attribute_is_dropped(), change the data structure definition:
the joinaliasvars list of a JOIN RTE must have a NULL Const instead
of a Var at any position that references a now-dropped column.  This
costs nothing during normal parse-rewrite-plan path, and instead we
have a linear-time update to make when loading a stored rule that
might contain now-dropped columns.  While at it, move the responsibility
for acquring locks on relations referenced by rules into this separate
function (which I therefore chose to call AcquireRewriteLocks).
This saves effort --- namely, duplicated lock grabs in parser and rewriter
--- in the normal path at a cost of one extra non-locked heap_open()
in the stored-rule path; seems a good tradeoff.  A fringe benefit is
that it is now *much* clearer that we acquire lock on relations referenced
in rules before we make any rewriter decisions based on their properties.
(I don't know of any bug of that ilk, but it wasn't exactly clear before.)

12 files changed:
src/backend/commands/explain.c
src/backend/commands/portalcmds.c
src/backend/commands/prepare.c
src/backend/optimizer/util/var.c
src/backend/parser/parse_relation.c
src/backend/rewrite/rewriteHandler.c
src/backend/tcop/postgres.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/parsenodes.h
src/include/parser/parsetree.h
src/include/rewrite/rewriteHandler.h
src/include/tcop/tcopprot.h

index d3dfe9edf7e53510d90dc56041a46728510d3d43..af3b5fb0f5b995e7c16d9705e686f9a71d1fc5ae 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.135 2005/04/25 01:30:12 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.136 2005/06/03 23:05:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -100,6 +100,12 @@ ExplainQuery(ExplainStmt *stmt, DestReceiver *dest)
        }
        else
        {
+               /*
+                * Must acquire locks in case we didn't come fresh from the parser.
+                * XXX this also scribbles on query, another reason for copyObject
+                */
+               AcquireRewriteLocks(query);
+
                /* Rewrite through rule system */
                rewritten = QueryRewrite(query);
 
@@ -166,6 +172,8 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
                        cursorOptions = dcstmt->options;
                        /* Still need to rewrite cursor command */
                        Assert(query->commandType == CMD_SELECT);
+                       /* get locks (we assume ExplainQuery already copied tree) */
+                       AcquireRewriteLocks(query);
                        rewritten = QueryRewrite(query);
                        if (list_length(rewritten) != 1)
                                elog(ERROR, "unexpected rewrite result");
index affbe2b3e4998b579fc48f46a9718876b7cc107c..0ff536661360462dcf43b8955bed2b60b92197db 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.41 2005/04/28 21:47:11 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.42 2005/06/03 23:05:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,6 +76,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
         * query, so we are not expecting rule rewriting to do anything
         * strange.
         */
+       AcquireRewriteLocks(query);
        rewritten = QueryRewrite(query);
        if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
                elog(ERROR, "unexpected rewrite result");
index 16aae6869388b75caf6d1c272b5c789834e70ca2..be3416bd3e5bbdc32ba8569ba562a9f1869add19 100644 (file)
@@ -10,7 +10,7 @@
  * Copyright (c) 2002-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.38 2005/05/29 04:23:03 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.39 2005/06/03 23:05:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -99,6 +99,7 @@ PrepareQuery(PrepareStmt *stmt)
        query = copyObject(stmt->query);
 
        /* Rewrite the query. The result could be 0, 1, or many queries. */
+       AcquireRewriteLocks(query);
        query_list = QueryRewrite(query);
 
        /* Generate plans for queries.  Snapshot is already set. */
index 3a6e7b216a9ec17c09f026172241870500eecc31..a95f7dcd76325889613937d3fa26d30939f729c5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.63 2004/12/31 22:00:23 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.64 2005/06/03 23:05:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -524,9 +524,7 @@ flatten_join_alias_vars_mutator(Node *node,
                                newvar = (Node *) lfirst(l);
                                attnum++;
                                /* Ignore dropped columns */
-                               if (get_rte_attribute_is_dropped(context->root->rtable,
-                                                                                                var->varno,
-                                                                                                attnum))
+                               if (IsA(newvar, Const))
                                        continue;
 
                                /*
index bb32ebdf1c1d20642194d93c13cd87ad9af0fc3e..25205d8894e9f9157d8b9400b2f218661c3988ae 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.108 2005/05/29 17:10:23 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.109 2005/06/03 23:05:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1202,8 +1202,7 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
  * results.  If include_dropped is TRUE then empty strings and NULL constants
  * (not Vars!) are returned for dropped columns.
  *
- * The target RTE is the rtindex'th entry of rtable.  (The whole rangetable
- * must be passed since we need it to determine dropped-ness for JOIN columns.)
+ * The target RTE is the rtindex'th entry of rtable.
  * sublevels_up is the varlevelsup value to use in the created Vars.
  *
  * The output lists go into *colnames and *colvars.
@@ -1358,6 +1357,8 @@ expandRTE(List *rtable, int rtindex, int sublevels_up,
                                varattno = 0;
                                forboth(colname, rte->eref->colnames, aliasvar, rte->joinaliasvars)
                                {
+                                       Node       *avar = (Node *) lfirst(aliasvar);
+
                                        varattno++;
 
                                        /*
@@ -1365,26 +1366,19 @@ expandRTE(List *rtable, int rtindex, int sublevels_up,
                                         * deleted columns in the join; but we have to check
                                         * since this routine is also used by the rewriter,
                                         * and joins found in stored rules might have join
-                                        * columns for since-deleted columns.
+                                        * columns for since-deleted columns.  This will be
+                                        * signaled by a NULL Const in the alias-vars list.
                                         */
-                                       if (get_rte_attribute_is_dropped(rtable, rtindex,
-                                                                                                        varattno))
+                                       if (IsA(avar, Const))
                                        {
                                                if (include_dropped)
                                                {
                                                        if (colnames)
                                                                *colnames = lappend(*colnames,
-                                                                                               makeString(pstrdup("")));
+                                                                                                       makeString(pstrdup("")));
                                                        if (colvars)
-                                                       {
-                                                               /*
-                                                                * can't use atttypid here, but it doesn't
-                                                                * really matter what type the Const
-                                                                * claims to be.
-                                                                */
                                                                *colvars = lappend(*colvars,
-                                                                                                makeNullConst(INT4OID));
-                                                       }
+                                                                                                  copyObject(avar));
                                                }
                                                continue;
                                        }
@@ -1399,7 +1393,6 @@ expandRTE(List *rtable, int rtindex, int sublevels_up,
 
                                        if (colvars)
                                        {
-                                               Node       *avar = (Node *) lfirst(aliasvar);
                                                Var                *varnode;
 
                                                varnode = makeVar(rtindex, varattno,
@@ -1711,9 +1704,8 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
  *             Check whether attempted attribute ref is to a dropped column
  */
 bool
-get_rte_attribute_is_dropped(List *rtable, int rtindex, AttrNumber attnum)
+get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
 {
-       RangeTblEntry *rte = rt_fetch(rtindex, rtable);
        bool            result;
 
        switch (rte->rtekind)
@@ -1750,8 +1742,8 @@ get_rte_attribute_is_dropped(List *rtable, int rtindex, AttrNumber attnum)
                                 * constructed, but one in a stored rule might contain
                                 * columns that were dropped from the underlying tables,
                                 * if said columns are nowhere explicitly referenced in
-                                * the rule.  So we have to recursively look at the
-                                * referenced column.
+                                * the rule.  This will be signaled to us by a NULL Const
+                                * in the joinaliasvars list.
                                 */
                                Var                *aliasvar;
 
@@ -1760,18 +1752,7 @@ get_rte_attribute_is_dropped(List *rtable, int rtindex, AttrNumber attnum)
                                        elog(ERROR, "invalid varattno %d", attnum);
                                aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
 
-                               /*
-                                * If the list item isn't a simple Var, then it must
-                                * represent a merged column, ie a USING column, and so it
-                                * couldn't possibly be dropped (since it's referenced in
-                                * the join clause).
-                                */
-                               if (!IsA(aliasvar, Var))
-                                       result = false;
-                               else
-                                       result = get_rte_attribute_is_dropped(rtable,
-                                                                                                                 aliasvar->varno,
-                                                                                                        aliasvar->varattno);
+                               result = IsA(aliasvar, Const);
                        }
                        break;
                case RTE_FUNCTION:
index 73c8dbab12740b7834141c48410ff3ab38a9296d..3c267ccdcd25b039b2b6242e892acecce0335d53 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.152 2005/05/29 18:34:57 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.153 2005/06/03 23:05:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,7 @@ typedef struct rewrite_event
        CmdType         event;                  /* type of rule being fired */
 } rewrite_event;
 
+static bool acquireLocksOnSubLinks(Node *node, void *context);
 static Query *rewriteRuleAction(Query *parsetree,
                                  Query *rule_action,
                                  Node *rule_qual,
@@ -57,6 +58,181 @@ static List *matchLocks(CmdType event, RuleLock *rulelocks,
 static Query *fireRIRrules(Query *parsetree, List *activeRIRs);
 
 
+/*
+ * AcquireRewriteLocks -
+ *       Acquire suitable locks on all the relations mentioned in the Query.
+ *       These locks will ensure that the relation schemas don't change under us
+ *       while we are rewriting and planning the query.
+ *
+ * A secondary purpose of this routine is to fix up JOIN RTE references to
+ * dropped columns (see details below).  Because the RTEs are modified in
+ * place, it is generally appropriate for the caller of this routine to have
+ * first done a copyObject() to make a writable copy of the querytree in the
+ * current memory context.
+ *
+ * This processing can, and for efficiency's sake should, be skipped when the
+ * querytree has just been built by the parser: parse analysis already got
+ * all the same locks we'd get here, and the parser will have omitted dropped
+ * columns from JOINs to begin with.  But we must do this whenever we are
+ * dealing with a querytree produced earlier than the current command.
+ *
+ * About JOINs and dropped columns: although the parser never includes an
+ * already-dropped column in a JOIN RTE's alias var list, it is possible for
+ * such a list in a stored rule to include references to dropped columns.
+ * (If the column is not explicitly referenced anywhere else in the query,
+ * the dependency mechanism won't consider it used by the rule and so won't
+ * prevent the column drop.)  To support get_rte_attribute_is_dropped(),
+ * we replace join alias vars that reference dropped columns with NULL Const
+ * nodes.
+ *
+ * (In PostgreSQL 8.0, we did not do this processing but instead had
+ * get_rte_attribute_is_dropped() recurse to detect dropped columns in joins.
+ * That approach had horrible performance unfortunately; in particular
+ * construction of a nested join was O(N^2) in the nesting depth.)
+ */
+void
+AcquireRewriteLocks(Query *parsetree)
+{
+       ListCell   *l;
+       int                     rt_index;
+
+       /*
+        * First, process RTEs of the current query level.
+        */
+       rt_index = 0;
+       foreach(l, parsetree->rtable)
+       {
+               RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+               Relation        rel;
+               LOCKMODE        lockmode;
+               List       *newaliasvars;
+               ListCell   *ll;
+
+               ++rt_index;
+               switch (rte->rtekind)
+               {
+                       case RTE_RELATION:
+                               /*
+                                * Grab the appropriate lock type for the relation, and
+                                * do not release it until end of transaction. This protects
+                                * the rewriter and planner against schema changes mid-query.
+                                *
+                                * If the relation is the query's result relation, then we
+                                * need RowExclusiveLock.  Otherwise, check to see if the
+                                * relation is accessed FOR UPDATE/SHARE or not.  We can't
+                                * just grab AccessShareLock because then the executor
+                                * would be trying to upgrade the lock, leading to possible
+                                * deadlocks.
+                                */
+                               if (rt_index == parsetree->resultRelation)
+                                       lockmode = RowExclusiveLock;
+                               else if (list_member_int(parsetree->rowMarks, rt_index))
+                                       lockmode = RowShareLock;
+                               else
+                                       lockmode = AccessShareLock;
+
+                               rel = heap_open(rte->relid, lockmode);
+                               heap_close(rel, NoLock);
+                               break;
+
+                       case RTE_JOIN:
+                               /*
+                                * Scan the join's alias var list to see if any columns
+                                * have been dropped, and if so replace those Vars with
+                                * NULL Consts.
+                                */
+                               newaliasvars = NIL;
+                               foreach(ll, rte->joinaliasvars)
+                               {
+                                       Var                *aliasvar = (Var *) lfirst(ll);
+
+                                       /*
+                                        * If the list item isn't a simple Var, then it must
+                                        * represent a merged column, ie a USING column, and so it
+                                        * couldn't possibly be dropped, since it's referenced in
+                                        * the join clause.  (Conceivably it could also be a
+                                        * NULL constant already?  But that's OK too.)
+                                        */
+                                       if (IsA(aliasvar, Var))
+                                       {
+                                               /*
+                                                * The elements of an alias list have to refer to
+                                                * earlier RTEs of the same rtable, because that's
+                                                * the order the planner builds things in.  So we
+                                                * already processed the referenced RTE, and so it's
+                                                * safe to use get_rte_attribute_is_dropped on it.
+                                                * (This might not hold after rewriting or planning,
+                                                * but it's OK to assume here.)
+                                                */
+                                               Assert(aliasvar->varlevelsup == 0);
+                                               if (aliasvar->varno >= rt_index)
+                                                       elog(ERROR, "unexpected varno %d in JOIN RTE %d",
+                                                                aliasvar->varno, rt_index);
+                                               if (get_rte_attribute_is_dropped(
+                                                       rt_fetch(aliasvar->varno, parsetree->rtable),
+                                                       aliasvar->varattno))
+                                               {
+                                                       /*
+                                                        * can't use vartype here, since that might be a
+                                                        * now-dropped type OID, but it doesn't really
+                                                        * matter what type the Const claims to be.
+                                                        */
+                                                       aliasvar = (Var *) makeNullConst(INT4OID);
+                                               }
+                                       }
+                                       newaliasvars = lappend(newaliasvars, aliasvar);
+                               }
+                               rte->joinaliasvars = newaliasvars;
+                               break;
+
+                       case RTE_SUBQUERY:
+                               /*
+                                * The subquery RTE itself is all right, but we have to
+                                * recurse to process the represented subquery.
+                                */
+                               AcquireRewriteLocks(rte->subquery);
+                               break;
+
+                       default:
+                               /* ignore other types of RTEs */
+                               break;
+               }
+       }
+
+       /*
+        * Recurse into sublink subqueries, too.  But we already did the ones
+        * in the rtable.
+        */
+       if (parsetree->hasSubLinks)
+               query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,
+                                                 QTW_IGNORE_RT_SUBQUERIES);
+}
+
+/*
+ * Walker to find sublink subqueries for AcquireRewriteLocks
+ */
+static bool
+acquireLocksOnSubLinks(Node *node, void *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, SubLink))
+       {
+               SubLink    *sub = (SubLink *) node;
+
+               /* Do what we came for */
+               AcquireRewriteLocks((Query *) sub->subselect);
+               /* Fall through to process lefthand args of SubLink */
+       }
+
+       /*
+        * Do NOT recurse into Query nodes, because AcquireRewriteLocks already
+        * processed subselects of subselects for us.
+        */
+       return expression_tree_walker(node, acquireLocksOnSubLinks, context);
+}
+
+
 /*
  * rewriteRuleAction -
  *       Rewrite the rule action with appropriate qualifiers (taken from
@@ -82,6 +258,12 @@ rewriteRuleAction(Query *parsetree,
        rule_action = (Query *) copyObject(rule_action);
        rule_qual = (Node *) copyObject(rule_qual);
 
+       /*
+        * Acquire necessary locks and fix any deleted JOIN RTE entries.
+        */
+       AcquireRewriteLocks(rule_action);
+       (void) acquireLocksOnSubLinks(rule_qual, NULL);
+
        current_varno = rt_index;
        rt_length = list_length(parsetree->rtable);
        new_varno = PRS2_NEW_VARNO + rt_length;
@@ -693,6 +875,9 @@ matchLocks(CmdType event,
 }
 
 
+/*
+ * ApplyRetrieveRule - expand an ON SELECT rule
+ */
 static Query *
 ApplyRetrieveRule(Query *parsetree,
                                  RewriteRule *rule,
@@ -713,11 +898,16 @@ ApplyRetrieveRule(Query *parsetree,
                elog(ERROR, "cannot handle per-attribute ON SELECT rule");
 
        /*
-        * Make a modifiable copy of the view query, and recursively expand
-        * any view references inside it.
+        * Make a modifiable copy of the view query, and acquire needed locks
+        * on the relations it mentions.
         */
        rule_action = copyObject(linitial(rule->actions));
 
+       AcquireRewriteLocks(rule_action);
+
+       /*
+        * Recursively expand any view references inside the view.
+        */
        rule_action = fireRIRrules(rule_action, activeRIRs);
 
        /*
@@ -868,7 +1058,6 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
                List       *locks;
                RuleLock   *rules;
                RewriteRule *rule;
-               LOCKMODE        lockmode;
                int                     i;
 
                ++rt_index;
@@ -904,26 +1093,10 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
                        continue;
 
                /*
-                * This may well be the first access to the relation during the
-                * current statement (it will be, if this Query was extracted from
-                * a rule or somehow got here other than via the parser).
-                * Therefore, grab the appropriate lock type for the relation, and
-                * do not release it until end of transaction.  This protects the
-                * rewriter and planner against schema changes mid-query.
-                *
-                * If the relation is the query's result relation, then
-                * RewriteQuery() already got the right lock on it, so we need no
-                * additional lock. Otherwise, check to see if the relation is
-                * accessed FOR UPDATE/SHARE or not.
+                * We can use NoLock here since either the parser or
+                * AcquireRewriteLocks should have locked the rel already.
                 */
-               if (rt_index == parsetree->resultRelation)
-                       lockmode = NoLock;
-               else if (list_member_int(parsetree->rowMarks, rt_index))
-                       lockmode = RowShareLock;
-               else
-                       lockmode = AccessShareLock;
-
-               rel = heap_open(rte->relid, lockmode);
+               rel = heap_open(rte->relid, NoLock);
 
                /*
                 * Collect the RIR rules that we must apply
@@ -1015,9 +1188,17 @@ CopyAndAddInvertedQual(Query *parsetree,
                                           int rt_index,
                                           CmdType event)
 {
-       Query      *new_tree = (Query *) copyObject(parsetree);
+       /* Don't scribble on the passed qual (it's in the relcache!) */
        Node       *new_qual = (Node *) copyObject(rule_qual);
 
+       /*
+        * In case there are subqueries in the qual, acquire necessary locks and
+        * fix any deleted JOIN RTE entries.  (This is somewhat redundant with
+        * rewriteRuleAction, but not entirely ... consider restructuring so
+        * that we only need to process the qual this way once.)
+        */
+       (void) acquireLocksOnSubLinks(new_qual, NULL);
+
        /* Fix references to OLD */
        ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
        /* Fix references to NEW */
@@ -1030,9 +1211,9 @@ CopyAndAddInvertedQual(Query *parsetree,
                                                          event,
                                                          rt_index);
        /* And attach the fixed qual */
-       AddInvertedQual(new_tree, new_qual);
+       AddInvertedQual(parsetree, new_qual);
 
-       return new_tree;
+       return parsetree;
 }
 
 
@@ -1112,7 +1293,7 @@ fireRules(Query *parsetree,
                        if (!*instead_flag)
                        {
                                if (*qual_product == NULL)
-                                       *qual_product = parsetree;
+                                       *qual_product = copyObject(parsetree);
                                *qual_product = CopyAndAddInvertedQual(*qual_product,
                                                                                                           event_qual,
                                                                                                           rt_index,
@@ -1177,15 +1358,10 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
                Assert(rt_entry->rtekind == RTE_RELATION);
 
                /*
-                * This may well be the first access to the result relation during
-                * the current statement (it will be, if this Query was extracted
-                * from a rule or somehow got here other than via the parser).
-                * Therefore, grab the appropriate lock type for a result
-                * relation, and do not release it until end of transaction.  This
-                * protects the rewriter and planner against schema changes
-                * mid-query.
+                * We can use NoLock here since either the parser or
+                * AcquireRewriteLocks should have locked the rel already.
                 */
-               rt_entry_relation = heap_open(rt_entry->relid, RowExclusiveLock);
+               rt_entry_relation = heap_open(rt_entry->relid, NoLock);
 
                /*
                 * If it's an INSERT or UPDATE, rewrite the targetlist into
@@ -1251,7 +1427,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
                        }
                }
 
-               heap_close(rt_entry_relation, NoLock);  /* keep lock! */
+               heap_close(rt_entry_relation, NoLock);
        }
 
        /*
@@ -1295,8 +1471,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
  *       Rewrite one query via query rewrite system, possibly returning 0
  *       or many queries.
  *
- * NOTE: The code in QueryRewrite was formerly in pg_parse_and_plan(), and was
- * moved here so that it would be invoked during EXPLAIN.
+ * NOTE: the parsetree must either have come straight from the parser,
+ * or have been scanned by AcquireRewriteLocks to acquire suitable locks.
  */
 List *
 QueryRewrite(Query *parsetree)
index 297eb75d8909812306771919b39254ca0d0eecc8..58b1ffbce9aa4890777df5beb724b16a0f54f38d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.446 2005/06/02 21:03:24 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.447 2005/06/03 23:05:29 tgl Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -158,6 +158,7 @@ static int  SocketBackend(StringInfo inBuf);
 static int     ReadCommand(StringInfo inBuf);
 static bool log_after_parse(List *raw_parsetree_list,
                                const char *query_string, char **prepare_string);
+static List *pg_rewrite_queries(List *querytree_list);
 static void start_xact_command(void);
 static void finish_xact_command(void);
 static void SigHupHandler(SIGNAL_ARGS);
@@ -642,8 +643,11 @@ pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams)
 
 /*
  * Perform rewriting of a list of queries produced by parse analysis.
+ *
+ * Note: queries must just have come from the parser, because we do not do
+ * AcquireRewriteLocks() on them.
  */
-List *
+static List *
 pg_rewrite_queries(List *querytree_list)
 {
        List       *new_list = NIL;
index be53f7373d9ce5274941e9a3cd6c53fe4d52a592..c6de4df714a2600f0fcbc78f554230bb4e5bdc39 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.198 2005/05/31 03:03:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.199 2005/06/03 23:05:29 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -67,6 +67,7 @@
 #include "parser/parse_oper.h"
 #include "parser/parse_type.h"
 #include "parser/parsetree.h"
+#include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rewriteSupport.h"
 #include "utils/array.h"
@@ -1661,6 +1662,9 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                 */
                query = getInsertSelectQuery(query, NULL);
 
+               /* Must acquire locks right away; see notes in get_query_def() */
+               AcquireRewriteLocks(query);
+
                context.buf = buf;
                context.namespaces = list_make1(&dpns);
                context.varprefix = (list_length(query->rtable) != 1);
@@ -1795,6 +1799,14 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
        deparse_context context;
        deparse_namespace dpns;
 
+       /*
+        * Before we begin to examine the query, acquire locks on referenced
+        * relations, and fix up deleted columns in JOIN RTEs.  This ensures
+        * consistent results.  Note we assume it's OK to scribble on the
+        * passed querytree!
+        */
+       AcquireRewriteLocks(query);
+
        context.buf = buf;
        context.namespaces = lcons(&dpns, list_copy(parentnamespace));
        context.varprefix = (parentnamespace != NIL ||
@@ -4245,6 +4257,7 @@ get_from_clause_alias(Alias *alias, int varno,
                                          Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
+       RangeTblEntry *rte = rt_fetch(varno, query->rtable);
        ListCell   *col;
        AttrNumber      attnum;
        bool            first = true;
@@ -4256,7 +4269,7 @@ get_from_clause_alias(Alias *alias, int varno,
        foreach(col, alias->colnames)
        {
                attnum++;
-               if (get_rte_attribute_is_dropped(query->rtable, varno, attnum))
+               if (get_rte_attribute_is_dropped(rte, attnum))
                        continue;
                if (first)
                {
index 97d5df935ac13cd4052a6d581e6faf870c63aadd..1208def12ce63674683d0bce0c19a62e92726e6b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.278 2005/04/28 21:47:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.279 2005/06/03 23:05:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -465,7 +465,10 @@ typedef struct DefElem
  *       those columns are known to be dropped at parse time.  Again, however,
  *       a stored rule might contain entries for columns dropped since the rule
  *       was created.  (This is only possible for columns not actually referenced
- *       in the rule.)
+ *       in the rule.)  When loading a stored rule, we replace the joinaliasvars
+ *       items for any such columns with NULL Consts.  (We can't simply delete
+ *       them from the joinaliasvars list, because that would affect the attnums
+ *       of Vars referencing the rest of the list.)
  *
  *       inh is TRUE for relation references that should be expanded to include
  *       inheritance children, if the rel has any.  This *must* be FALSE for
@@ -535,7 +538,10 @@ typedef struct RangeTblEntry
         * to the columns of the join result.  An alias Var referencing column
         * K of the join result can be replaced by the K'th element of
         * joinaliasvars --- but to simplify the task of reverse-listing
-        * aliases correctly, we do not do that until planning time.
+        * aliases correctly, we do not do that until planning time.  In a Query
+        * loaded from a stored rule, it is also possible for joinaliasvars
+        * items to be NULL Consts, denoting columns dropped since the rule was
+        * made.
         */
        JoinType        jointype;               /* type of join */
        List       *joinaliasvars;      /* list of alias-var expansions */
index c6911e9a2f8a1d505c908a4d9d538e1c8ebf4e79..c1997bb787ca4c5b9891156c11f2ffbf9b974b40 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.29 2004/12/31 22:03:38 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.30 2005/06/03 23:05:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,8 +59,8 @@ extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
  * Check whether an attribute of an RTE has been dropped (note that
  * get_rte_attribute_type will fail on such an attr)
  */
-extern bool get_rte_attribute_is_dropped(List *rtable, int rtindex,
-                                                        AttrNumber attnum);
+extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
+                                                                                AttrNumber attnum);
 
 
 /* ----------------
index 566ab62c759525928fbb464bd5c911ed0aaef082..478dbdd0bab0366380659f7337a11fa6547073c3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/rewrite/rewriteHandler.h,v 1.24 2004/12/31 22:03:41 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/rewrite/rewriteHandler.h,v 1.25 2005/06/03 23:05:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 #include "nodes/parsenodes.h"
 
 extern List *QueryRewrite(Query *parsetree);
+extern void AcquireRewriteLocks(Query *parsetree);
 extern Node *build_column_default(Relation rel, int attrno);
 
 #endif   /* REWRITEHANDLER_H */
index 23b338e7ae1ba7469e904387b7752b87e14fbaf2..d694146d59ae1e69105df913d0dd602f31adc89a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.74 2005/06/02 21:03:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.75 2005/06/03 23:05:30 tgl Exp $
  *
  * OLD COMMENTS
  *       This file was created so that other c files could get the two
@@ -48,7 +48,6 @@ extern List *pg_parse_and_rewrite(const char *query_string,
 extern List *pg_parse_query(const char *query_string);
 extern List *pg_analyze_and_rewrite(Node *parsetree,
                                           Oid *paramTypes, int numParams);
-extern List *pg_rewrite_queries(List *querytree_list);
 extern Plan *pg_plan_query(Query *querytree, ParamListInfo boundParams);
 extern List *pg_plan_queries(List *querytrees, ParamListInfo boundParams,
                                bool needSnapshot);