+static Query *fireRIRrules(Query *parsetree, List *activeRIRs,
+ bool forUpdatePushedDown);
+
+
+/*
+ * 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.
+ *
+ * forUpdatePushedDown indicates that a pushed-down FOR UPDATE/SHARE applies
+ * to the current subquery, requiring all rels to be opened with RowShareLock.
+ * This should always be false at the start of the recursion.
+ *
+ * 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, bool forUpdatePushedDown)
+{
+ 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;
+ Index curinputvarno;
+ RangeTblEntry *curinputrte;
+ 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 (forUpdatePushedDown ||
+ get_parse_rowmark(parsetree, rt_index) != NULL)
+ 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.
+ *
+ * Since a join has only two inputs, we can expect to see
+ * multiple references to the same input RTE; optimize away
+ * multiple fetches.
+ */
+ newaliasvars = NIL;
+ curinputvarno = 0;
+ curinputrte = NULL;
+ 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 != curinputvarno)
+ {
+ curinputvarno = aliasvar->varno;
+ if (curinputvarno >= rt_index)
+ elog(ERROR, "unexpected varno %d in JOIN RTE %d",
+ curinputvarno, rt_index);
+ curinputrte = rt_fetch(curinputvarno,
+ parsetree->rtable);
+ }
+ if (get_rte_attribute_is_dropped(curinputrte,
+ 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, -1);
+ }
+ }
+ 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,
+ (forUpdatePushedDown ||
+ get_parse_rowmark(parsetree, rt_index) != NULL));
+ break;
+
+ default:
+ /* ignore other types of RTEs */
+ break;
+ }
+ }
+
+ /* Recurse into subqueries in WITH */
+ foreach(l, parsetree->cteList)
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
+
+ AcquireRewriteLocks((Query *) cte->ctequery, false);
+ }
+
+ /*
+ * Recurse into sublink subqueries, too. But we already did the ones in
+ * the rtable and cteList.
+ */
+ if (parsetree->hasSubLinks)
+ query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,
+ QTW_IGNORE_RC_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, false);
+ /* 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);
+}