From ba42002461a82ff5aba240611fe5a2366b0f1f86 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 3 Jun 2005 23:05:30 +0000 Subject: [PATCH] Revise handling of dropped columns in JOIN alias lists to avoid a 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.) --- src/backend/commands/explain.c | 10 +- src/backend/commands/portalcmds.c | 3 +- src/backend/commands/prepare.c | 3 +- src/backend/optimizer/util/var.c | 6 +- src/backend/parser/parse_relation.c | 45 ++--- src/backend/rewrite/rewriteHandler.c | 252 +++++++++++++++++++++++---- src/backend/tcop/postgres.c | 8 +- src/backend/utils/adt/ruleutils.c | 17 +- src/include/nodes/parsenodes.h | 12 +- src/include/parser/parsetree.h | 6 +- src/include/rewrite/rewriteHandler.h | 3 +- src/include/tcop/tcopprot.h | 3 +- 12 files changed, 278 insertions(+), 90 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index d3dfe9edf7..af3b5fb0f5 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -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"); diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index affbe2b3e4..0ff5366613 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -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"); diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 16aae68693..be3416bd3e 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -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. */ diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 3a6e7b216a..a95f7dcd76 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -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; /* diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index bb32ebdf1c..25205d8894 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -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: diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 73c8dbab12..3c267ccdcd 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -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) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 297eb75d89..58b1ffbce9 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -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; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index be53f7373d..c6de4df714 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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) { diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 97d5df935a..1208def12c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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 */ diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h index c6911e9a2f..c1997bb787 100644 --- a/src/include/parser/parsetree.h +++ b/src/include/parser/parsetree.h @@ -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); /* ---------------- diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 566ab62c75..478dbdd0ba 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -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 */ diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index 23b338e7ae..d694146d59 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -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); -- 2.40.0