From 82d8ab6fc4c1a0330c91022728e1e766db207069 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 11 Oct 2007 18:05:27 +0000 Subject: [PATCH] Fix the plan-invalidation mechanism to treat regclass constants that refer to a relation as a reason to invalidate a plan when the relation changes. This handles scenarios such as dropping/recreating a sequence that is referenced by nextval('seq') in a cached plan. Rather than teach plancache.c all about digging through plan trees to find regclass Consts, we charge the planner's setrefs.c with making a list of the relation OIDs on which each plan depends. That way the list can be built cheaply during a plan tree traversal that has to happen anyway. Per bug #3662 and subsequent discussion. --- src/backend/nodes/copyfuncs.c | 3 +- src/backend/nodes/outfuncs.c | 4 +- src/backend/optimizer/plan/planner.c | 7 +- src/backend/optimizer/plan/setrefs.c | 245 ++++++++++++++++-------- src/backend/utils/cache/plancache.c | 21 +- src/include/nodes/plannodes.h | 4 +- src/include/nodes/relation.h | 4 +- src/include/optimizer/planmain.h | 5 +- src/test/regress/expected/plancache.out | 17 ++ src/test/regress/sql/plancache.sql | 14 ++ 10 files changed, 223 insertions(+), 101 deletions(-) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index b6c6331d17..c6393effcd 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.382 2007/09/03 18:46:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.383 2007/10/11 18:05:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -83,6 +83,7 @@ _copyPlannedStmt(PlannedStmt *from) COPY_BITMAPSET_FIELD(rewindPlanIDs); COPY_NODE_FIELD(returningLists); COPY_NODE_FIELD(rowMarks); + COPY_NODE_FIELD(relationOids); COPY_SCALAR_FIELD(nParamExec); return newnode; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index db0bf7c050..005879b8c9 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.314 2007/08/31 01:44:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.315 2007/10/11 18:05:27 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -250,6 +250,7 @@ _outPlannedStmt(StringInfo str, PlannedStmt *node) WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(returningLists); WRITE_NODE_FIELD(rowMarks); + WRITE_NODE_FIELD(relationOids); WRITE_INT_FIELD(nParamExec); } @@ -1300,6 +1301,7 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node) WRITE_NODE_FIELD(subrtables); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(relationOids); } static void diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index e36ba97f6b..c55f89da78 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.222 2007/09/20 17:56:31 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.223 2007/10/11 18:05:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -134,6 +134,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) glob->subrtables = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->relationOids = NIL; glob->transientPlan = false; /* Determine what fraction of the plan is likely to be scanned */ @@ -194,6 +195,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) result->rewindPlanIDs = glob->rewindPlanIDs; result->returningLists = root->returningLists; result->rowMarks = parse->rowMarks; + result->relationOids = glob->relationOids; result->nParamExec = list_length(glob->paramlist); return result; @@ -1184,7 +1186,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) List *rlist; Assert(parse->resultRelation); - rlist = set_returning_clause_references(parse->returningList, + rlist = set_returning_clause_references(root->glob, + parse->returningList, result_plan, parse->resultRelation); root->returningLists = list_make1(rlist); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 055b47beec..bc8ce00d4e 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -2,19 +2,20 @@ * * setrefs.c * Post-processing of a completed plan tree: fix references to subplan - * vars, and compute regproc values for operators + * vars, compute regproc values for operators, etc * * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.136 2007/06/11 01:16:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.137 2007/10/11 18:05:27 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" @@ -42,11 +43,13 @@ typedef struct typedef struct { + PlannerGlobal *glob; int rtoffset; } fix_scan_expr_context; typedef struct { + PlannerGlobal *glob; indexed_tlist *outer_itlist; indexed_tlist *inner_itlist; Index acceptable_rel; @@ -55,24 +58,25 @@ typedef struct typedef struct { + PlannerGlobal *glob; indexed_tlist *subplan_itlist; int rtoffset; } fix_upper_expr_context; -#define fix_scan_list(lst, rtoffset) \ - ((List *) fix_scan_expr((Node *) (lst), rtoffset)) +#define fix_scan_list(glob, lst, rtoffset) \ + ((List *) fix_scan_expr(glob, (Node *) (lst), rtoffset)) static Plan *set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset); static Plan *set_subqueryscan_references(PlannerGlobal *glob, SubqueryScan *plan, int rtoffset); static bool trivial_subqueryscan(SubqueryScan *plan); -static Node *fix_scan_expr(Node *node, int rtoffset); +static Node *fix_scan_expr(PlannerGlobal *glob, Node *node, int rtoffset); static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context); -static void set_join_references(Join *join, int rtoffset); -static void set_inner_join_references(Plan *inner_plan, +static void set_join_references(PlannerGlobal *glob, Join *join, int rtoffset); +static void set_inner_join_references(PlannerGlobal *glob, Plan *inner_plan, indexed_tlist *outer_itlist); -static void set_upper_references(Plan *plan, int rtoffset); +static void set_upper_references(PlannerGlobal *glob, Plan *plan, int rtoffset); static void set_dummy_tlist_references(Plan *plan, int rtoffset); static indexed_tlist *build_tlist_index(List *tlist); static Var *search_indexed_tlist_for_var(Var *var, @@ -82,13 +86,15 @@ static Var *search_indexed_tlist_for_var(Var *var, static Var *search_indexed_tlist_for_non_var(Node *node, indexed_tlist *itlist, Index newvarno); -static List *fix_join_expr(List *clauses, +static List *fix_join_expr(PlannerGlobal *glob, + List *clauses, indexed_tlist *outer_itlist, indexed_tlist *inner_itlist, Index acceptable_rel, int rtoffset); static Node *fix_join_expr_mutator(Node *node, fix_join_expr_context *context); -static Node *fix_upper_expr(Node *node, +static Node *fix_upper_expr(PlannerGlobal *glob, + Node *node, indexed_tlist *subplan_itlist, int rtoffset); static Node *fix_upper_expr_mutator(Node *node, @@ -120,6 +126,11 @@ static bool fix_opfuncids_walker(Node *node, void *context); * 4. We compute regproc OIDs for operators (ie, we look up the function * that implements each op). * + * 5. We create a list of OIDs of relations that the plan depends on. + * This will be used by plancache.c to drive invalidation of cached plans. + * (Someday we might want to generalize this to include other types of + * objects, but for now tracking relations seems to solve most problems.) + * * We also perform one final optimization step, which is to delete * SubqueryScan plan nodes that aren't doing anything useful (ie, have * no qual and a no-op targetlist). The reason for doing this last is that @@ -128,7 +139,7 @@ static bool fix_opfuncids_walker(Node *node, void *context); * wouldn't match up with the Vars in the outer plan tree. The SubqueryScan * serves a necessary function as a buffer between outer query and subquery * variable numbering ... but after we've flattened the rangetable this is - * no longer a problem, since there's only one rtindex namespace. + * no longer a problem, since then there's only one rtindex namespace. * * set_plan_references recursively traverses the whole plan tree. * @@ -140,7 +151,8 @@ static bool fix_opfuncids_walker(Node *node, void *context); * The return value is normally the same Plan node passed in, but can be * different when the passed-in Plan is a SubqueryScan we decide isn't needed. * - * The flattened rangetable entries are appended to glob->finalrtable. + * The flattened rangetable entries are appended to glob->finalrtable, and + * the list of relation OIDs is appended to glob->relationOids. * * Notice that we modify Plan nodes in-place, but use expression_tree_mutator * to process targetlist and qual expressions. We can assume that the Plan @@ -177,6 +189,22 @@ set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable) newrte->joinaliasvars = NIL; glob->finalrtable = lappend(glob->finalrtable, newrte); + + /* + * If it's a plain relation RTE, add the table to relationOids. + * + * We do this even though the RTE might be unreferenced in the + * plan tree; this would correspond to cases such as views that + * were expanded, child tables that were eliminated by constraint + * exclusion, etc. Schema invalidation on such a rel must still + * force rebuilding of the plan. + * + * Note we don't bother to avoid duplicate list entries. We could, + * but it would probably cost more cycles than it would save. + */ + if (newrte->rtekind == RTE_RELATION) + glob->relationOids = lappend_oid(glob->relationOids, + newrte->relid); } /* Now fix the Plan tree */ @@ -205,9 +233,9 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) splan->scanrelid += rtoffset; splan->plan.targetlist = - fix_scan_list(splan->plan.targetlist, rtoffset); + fix_scan_list(glob, splan->plan.targetlist, rtoffset); splan->plan.qual = - fix_scan_list(splan->plan.qual, rtoffset); + fix_scan_list(glob, splan->plan.qual, rtoffset); } break; case T_IndexScan: @@ -216,13 +244,13 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) splan->scan.scanrelid += rtoffset; splan->scan.plan.targetlist = - fix_scan_list(splan->scan.plan.targetlist, rtoffset); + fix_scan_list(glob, splan->scan.plan.targetlist, rtoffset); splan->scan.plan.qual = - fix_scan_list(splan->scan.plan.qual, rtoffset); + fix_scan_list(glob, splan->scan.plan.qual, rtoffset); splan->indexqual = - fix_scan_list(splan->indexqual, rtoffset); + fix_scan_list(glob, splan->indexqual, rtoffset); splan->indexqualorig = - fix_scan_list(splan->indexqualorig, rtoffset); + fix_scan_list(glob, splan->indexqualorig, rtoffset); } break; case T_BitmapIndexScan: @@ -234,9 +262,9 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) Assert(splan->scan.plan.targetlist == NIL); Assert(splan->scan.plan.qual == NIL); splan->indexqual = - fix_scan_list(splan->indexqual, rtoffset); + fix_scan_list(glob, splan->indexqual, rtoffset); splan->indexqualorig = - fix_scan_list(splan->indexqualorig, rtoffset); + fix_scan_list(glob, splan->indexqualorig, rtoffset); } break; case T_BitmapHeapScan: @@ -245,11 +273,11 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) splan->scan.scanrelid += rtoffset; splan->scan.plan.targetlist = - fix_scan_list(splan->scan.plan.targetlist, rtoffset); + fix_scan_list(glob, splan->scan.plan.targetlist, rtoffset); splan->scan.plan.qual = - fix_scan_list(splan->scan.plan.qual, rtoffset); + fix_scan_list(glob, splan->scan.plan.qual, rtoffset); splan->bitmapqualorig = - fix_scan_list(splan->bitmapqualorig, rtoffset); + fix_scan_list(glob, splan->bitmapqualorig, rtoffset); } break; case T_TidScan: @@ -258,11 +286,11 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) splan->scan.scanrelid += rtoffset; splan->scan.plan.targetlist = - fix_scan_list(splan->scan.plan.targetlist, rtoffset); + fix_scan_list(glob, splan->scan.plan.targetlist, rtoffset); splan->scan.plan.qual = - fix_scan_list(splan->scan.plan.qual, rtoffset); + fix_scan_list(glob, splan->scan.plan.qual, rtoffset); splan->tidquals = - fix_scan_list(splan->tidquals, rtoffset); + fix_scan_list(glob, splan->tidquals, rtoffset); } break; case T_SubqueryScan: @@ -276,11 +304,11 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) splan->scan.scanrelid += rtoffset; splan->scan.plan.targetlist = - fix_scan_list(splan->scan.plan.targetlist, rtoffset); + fix_scan_list(glob, splan->scan.plan.targetlist, rtoffset); splan->scan.plan.qual = - fix_scan_list(splan->scan.plan.qual, rtoffset); + fix_scan_list(glob, splan->scan.plan.qual, rtoffset); splan->funcexpr = - fix_scan_expr(splan->funcexpr, rtoffset); + fix_scan_expr(glob, splan->funcexpr, rtoffset); } break; case T_ValuesScan: @@ -289,18 +317,18 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) splan->scan.scanrelid += rtoffset; splan->scan.plan.targetlist = - fix_scan_list(splan->scan.plan.targetlist, rtoffset); + fix_scan_list(glob, splan->scan.plan.targetlist, rtoffset); splan->scan.plan.qual = - fix_scan_list(splan->scan.plan.qual, rtoffset); + fix_scan_list(glob, splan->scan.plan.qual, rtoffset); splan->values_lists = - fix_scan_list(splan->values_lists, rtoffset); + fix_scan_list(glob, splan->values_lists, rtoffset); } break; case T_NestLoop: case T_MergeJoin: case T_HashJoin: - set_join_references((Join *) plan, rtoffset); + set_join_references(glob, (Join *) plan, rtoffset); break; case T_Hash: @@ -337,14 +365,14 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) Assert(splan->plan.qual == NIL); splan->limitOffset = - fix_scan_expr(splan->limitOffset, rtoffset); + fix_scan_expr(glob, splan->limitOffset, rtoffset); splan->limitCount = - fix_scan_expr(splan->limitCount, rtoffset); + fix_scan_expr(glob, splan->limitCount, rtoffset); } break; case T_Agg: case T_Group: - set_upper_references(plan, rtoffset); + set_upper_references(glob, plan, rtoffset); break; case T_Result: { @@ -355,17 +383,17 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) * like a scan node than an upper node. */ if (splan->plan.lefttree != NULL) - set_upper_references(plan, rtoffset); + set_upper_references(glob, plan, rtoffset); else { splan->plan.targetlist = - fix_scan_list(splan->plan.targetlist, rtoffset); + fix_scan_list(glob, splan->plan.targetlist, rtoffset); splan->plan.qual = - fix_scan_list(splan->plan.qual, rtoffset); + fix_scan_list(glob, splan->plan.qual, rtoffset); } /* resconstantqual can't contain any subplan variable refs */ splan->resconstantqual = - fix_scan_expr(splan->resconstantqual, rtoffset); + fix_scan_expr(glob, splan->resconstantqual, rtoffset); } break; case T_Append: @@ -497,9 +525,9 @@ set_subqueryscan_references(PlannerGlobal *glob, */ plan->scan.scanrelid += rtoffset; plan->scan.plan.targetlist = - fix_scan_list(plan->scan.plan.targetlist, rtoffset); + fix_scan_list(glob, plan->scan.plan.targetlist, rtoffset); plan->scan.plan.qual = - fix_scan_list(plan->scan.plan.qual, rtoffset); + fix_scan_list(glob, plan->scan.plan.qual, rtoffset); result = (Plan *) plan; } @@ -585,14 +613,16 @@ copyVar(Var *var) * fix_scan_expr * Do set_plan_references processing on a scan-level expression * - * This consists of incrementing all Vars' varnos by rtoffset and - * looking up operator opcode info for OpExpr and related nodes. + * This consists of incrementing all Vars' varnos by rtoffset, + * looking up operator opcode info for OpExpr and related nodes, + * and adding OIDs from regclass Const nodes into glob->relationOids. */ static Node * -fix_scan_expr(Node *node, int rtoffset) +fix_scan_expr(PlannerGlobal *glob, Node *node, int rtoffset) { fix_scan_expr_context context; + context.glob = glob; context.rtoffset = rtoffset; return fix_scan_expr_mutator(node, &context); } @@ -639,6 +669,17 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ else if (IsA(node, ScalarArrayOpExpr)) set_sa_opfuncid((ScalarArrayOpExpr *) node); + else if (IsA(node, Const)) + { + Const *con = (Const *) node; + + /* Check for regclass reference */ + if (con->consttype == REGCLASSOID && !con->constisnull) + context->glob->relationOids = + lappend_oid(context->glob->relationOids, + DatumGetObjectId(con->constvalue)); + /* Fall through to let expression_tree_mutator copy it */ + } return expression_tree_mutator(node, fix_scan_expr_mutator, (void *) context); } @@ -649,14 +690,14 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context) * subplans, by setting the varnos to OUTER or INNER and setting attno * values to the result domain number of either the corresponding outer * or inner join tuple item. Also perform opcode lookup for these - * expressions. + * expressions. and add regclass OIDs to glob->relationOids. * * In the case of a nestloop with inner indexscan, we will also need to * apply the same transformation to any outer vars appearing in the * quals of the child indexscan. set_inner_join_references does that. */ static void -set_join_references(Join *join, int rtoffset) +set_join_references(PlannerGlobal *glob, Join *join, int rtoffset) { Plan *outer_plan = join->plan.lefttree; Plan *inner_plan = join->plan.righttree; @@ -667,17 +708,20 @@ set_join_references(Join *join, int rtoffset) inner_itlist = build_tlist_index(inner_plan->targetlist); /* All join plans have tlist, qual, and joinqual */ - join->plan.targetlist = fix_join_expr(join->plan.targetlist, + join->plan.targetlist = fix_join_expr(glob, + join->plan.targetlist, outer_itlist, inner_itlist, (Index) 0, rtoffset); - join->plan.qual = fix_join_expr(join->plan.qual, + join->plan.qual = fix_join_expr(glob, + join->plan.qual, outer_itlist, inner_itlist, (Index) 0, rtoffset); - join->joinqual = fix_join_expr(join->joinqual, + join->joinqual = fix_join_expr(glob, + join->joinqual, outer_itlist, inner_itlist, (Index) 0, @@ -687,13 +731,14 @@ set_join_references(Join *join, int rtoffset) if (IsA(join, NestLoop)) { /* This processing is split out to handle possible recursion */ - set_inner_join_references(inner_plan, outer_itlist); + set_inner_join_references(glob, inner_plan, outer_itlist); } else if (IsA(join, MergeJoin)) { MergeJoin *mj = (MergeJoin *) join; - mj->mergeclauses = fix_join_expr(mj->mergeclauses, + mj->mergeclauses = fix_join_expr(glob, + mj->mergeclauses, outer_itlist, inner_itlist, (Index) 0, @@ -703,7 +748,8 @@ set_join_references(Join *join, int rtoffset) { HashJoin *hj = (HashJoin *) join; - hj->hashclauses = fix_join_expr(hj->hashclauses, + hj->hashclauses = fix_join_expr(glob, + hj->hashclauses, outer_itlist, inner_itlist, (Index) 0, @@ -728,7 +774,8 @@ set_join_references(Join *join, int rtoffset) * recursion reaches the inner indexscan, and so we'd have done it twice. */ static void -set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) +set_inner_join_references(PlannerGlobal *glob, Plan *inner_plan, + indexed_tlist *outer_itlist) { if (IsA(inner_plan, IndexScan)) { @@ -747,12 +794,14 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) Index innerrel = innerscan->scan.scanrelid; /* only refs to outer vars get changed in the inner qual */ - innerscan->indexqualorig = fix_join_expr(indexqualorig, + innerscan->indexqualorig = fix_join_expr(glob, + indexqualorig, outer_itlist, NULL, innerrel, 0); - innerscan->indexqual = fix_join_expr(innerscan->indexqual, + innerscan->indexqual = fix_join_expr(glob, + innerscan->indexqual, outer_itlist, NULL, innerrel, @@ -764,7 +813,8 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * may get rechecked as qpquals). */ if (NumRelids((Node *) inner_plan->qual) > 1) - inner_plan->qual = fix_join_expr(inner_plan->qual, + inner_plan->qual = fix_join_expr(glob, + inner_plan->qual, outer_itlist, NULL, innerrel, @@ -785,12 +835,14 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) Index innerrel = innerscan->scan.scanrelid; /* only refs to outer vars get changed in the inner qual */ - innerscan->indexqualorig = fix_join_expr(indexqualorig, + innerscan->indexqualorig = fix_join_expr(glob, + indexqualorig, outer_itlist, NULL, innerrel, 0); - innerscan->indexqual = fix_join_expr(innerscan->indexqual, + innerscan->indexqual = fix_join_expr(glob, + innerscan->indexqual, outer_itlist, NULL, innerrel, @@ -814,7 +866,8 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) /* only refs to outer vars get changed in the inner qual */ if (NumRelids((Node *) bitmapqualorig) > 1) - innerscan->bitmapqualorig = fix_join_expr(bitmapqualorig, + innerscan->bitmapqualorig = fix_join_expr(glob, + bitmapqualorig, outer_itlist, NULL, innerrel, @@ -826,14 +879,15 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * get rechecked as qpquals). */ if (NumRelids((Node *) inner_plan->qual) > 1) - inner_plan->qual = fix_join_expr(inner_plan->qual, + inner_plan->qual = fix_join_expr(glob, + inner_plan->qual, outer_itlist, NULL, innerrel, 0); /* Now recurse */ - set_inner_join_references(inner_plan->lefttree, outer_itlist); + set_inner_join_references(glob, inner_plan->lefttree, outer_itlist); } else if (IsA(inner_plan, BitmapAnd)) { @@ -843,7 +897,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, innerscan->bitmapplans) { - set_inner_join_references((Plan *) lfirst(l), outer_itlist); + set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist); } } else if (IsA(inner_plan, BitmapOr)) @@ -854,7 +908,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, innerscan->bitmapplans) { - set_inner_join_references((Plan *) lfirst(l), outer_itlist); + set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist); } } else if (IsA(inner_plan, TidScan)) @@ -862,7 +916,8 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) TidScan *innerscan = (TidScan *) inner_plan; Index innerrel = innerscan->scan.scanrelid; - innerscan->tidquals = fix_join_expr(innerscan->tidquals, + innerscan->tidquals = fix_join_expr(glob, + innerscan->tidquals, outer_itlist, NULL, innerrel, @@ -879,7 +934,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) foreach(l, appendplan->appendplans) { - set_inner_join_references((Plan *) lfirst(l), outer_itlist); + set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist); } } else if (IsA(inner_plan, Result)) @@ -888,7 +943,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) Result *result = (Result *) inner_plan; if (result->plan.lefttree) - set_inner_join_references(result->plan.lefttree, outer_itlist); + set_inner_join_references(glob, result->plan.lefttree, outer_itlist); } } @@ -896,7 +951,8 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * set_upper_references * Update the targetlist and quals of an upper-level plan node * to refer to the tuples returned by its lefttree subplan. - * Also perform opcode lookup for these expressions. + * Also perform opcode lookup for these expressions, and + * add regclass OIDs to glob->relationOids. * * This is used for single-input plan types like Agg, Group, Result. * @@ -910,7 +966,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist) * the expression. */ static void -set_upper_references(Plan *plan, int rtoffset) +set_upper_references(PlannerGlobal *glob, Plan *plan, int rtoffset) { Plan *subplan = plan->lefttree; indexed_tlist *subplan_itlist; @@ -925,7 +981,8 @@ set_upper_references(Plan *plan, int rtoffset) TargetEntry *tle = (TargetEntry *) lfirst(l); Node *newexpr; - newexpr = fix_upper_expr((Node *) tle->expr, + newexpr = fix_upper_expr(glob, + (Node *) tle->expr, subplan_itlist, rtoffset); tle = flatCopyTargetEntry(tle); @@ -935,7 +992,8 @@ set_upper_references(Plan *plan, int rtoffset) plan->targetlist = output_targetlist; plan->qual = (List *) - fix_upper_expr((Node *) plan->qual, + fix_upper_expr(glob, + (Node *) plan->qual, subplan_itlist, rtoffset); @@ -1166,7 +1224,8 @@ search_indexed_tlist_for_non_var(Node *node, * Create a new set of targetlist entries or join qual clauses by * changing the varno/varattno values of variables in the clauses * to reference target list values from the outer and inner join - * relation target lists. Also perform opcode lookup. + * relation target lists. Also perform opcode lookup and add + * regclass OIDs to glob->relationOids. * * This is used in two different scenarios: a normal join clause, where * all the Vars in the clause *must* be replaced by OUTER or INNER references; @@ -1192,7 +1251,8 @@ search_indexed_tlist_for_non_var(Node *node, * not modified. */ static List * -fix_join_expr(List *clauses, +fix_join_expr(PlannerGlobal *glob, + List *clauses, indexed_tlist *outer_itlist, indexed_tlist *inner_itlist, Index acceptable_rel, @@ -1200,6 +1260,7 @@ fix_join_expr(List *clauses, { fix_join_expr_context context; + context.glob = glob; context.outer_itlist = outer_itlist; context.inner_itlist = inner_itlist; context.acceptable_rel = acceptable_rel; @@ -1276,6 +1337,17 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ else if (IsA(node, ScalarArrayOpExpr)) set_sa_opfuncid((ScalarArrayOpExpr *) node); + else if (IsA(node, Const)) + { + Const *con = (Const *) node; + + /* Check for regclass reference */ + if (con->consttype == REGCLASSOID && !con->constisnull) + context->glob->relationOids = + lappend_oid(context->glob->relationOids, + DatumGetObjectId(con->constvalue)); + /* Fall through to let expression_tree_mutator copy it */ + } return expression_tree_mutator(node, fix_join_expr_mutator, (void *) context); @@ -1284,7 +1356,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) /* * fix_upper_expr * Modifies an expression tree so that all Var nodes reference outputs - * of a subplan. Also performs opcode lookup. + * of a subplan. Also performs opcode lookup, and adds regclass OIDs to + * glob->relationOids. * * This is used to fix up target and qual expressions of non-join upper-level * plan nodes. @@ -1308,12 +1381,14 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) * The original tree is not modified. */ static Node * -fix_upper_expr(Node *node, +fix_upper_expr(PlannerGlobal *glob, + Node *node, indexed_tlist *subplan_itlist, int rtoffset) { fix_upper_expr_context context; + context.glob = glob; context.subplan_itlist = subplan_itlist; context.rtoffset = rtoffset; return fix_upper_expr_mutator(node, &context); @@ -1359,6 +1434,17 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ else if (IsA(node, ScalarArrayOpExpr)) set_sa_opfuncid((ScalarArrayOpExpr *) node); + else if (IsA(node, Const)) + { + Const *con = (Const *) node; + + /* Check for regclass reference */ + if (con->consttype == REGCLASSOID && !con->constisnull) + context->glob->relationOids = + lappend_oid(context->glob->relationOids, + DatumGetObjectId(con->constvalue)); + /* Fall through to let expression_tree_mutator copy it */ + } return expression_tree_mutator(node, fix_upper_expr_mutator, (void *) context); @@ -1376,7 +1462,8 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) * adjusted RETURNING list, result-table Vars will still have their * original varno, but Vars for other rels will have varno OUTER. * - * We also must perform opcode lookup. + * We also must perform opcode lookup and add regclass OIDs to + * glob->relationOids. * * 'rlist': the RETURNING targetlist to be fixed * 'topplan': the top Plan node for the query (not yet passed through @@ -1387,7 +1474,8 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) * they are not coming from a subplan. */ List * -set_returning_clause_references(List *rlist, +set_returning_clause_references(PlannerGlobal *glob, + List *rlist, Plan *topplan, Index resultRelation) { @@ -1402,7 +1490,8 @@ set_returning_clause_references(List *rlist, */ itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation); - rlist = fix_join_expr(rlist, + rlist = fix_join_expr(glob, + rlist, itlist, NULL, resultRelation, diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 43297281f5..2f52ed7a8c 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -33,7 +33,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.11 2007/09/20 17:56:31 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.12 2007/10/11 18:05:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -921,26 +921,17 @@ PlanCacheCallback(Datum arg, Oid relid) foreach(lc2, plan->stmt_list) { PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2); - ListCell *lc3; Assert(!IsA(plannedstmt, Query)); if (!IsA(plannedstmt, PlannedStmt)) continue; /* Ignore utility statements */ - foreach(lc3, plannedstmt->rtable) + if ((relid == InvalidOid) ? plannedstmt->relationOids != NIL : + list_member_oid(plannedstmt->relationOids, relid)) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc3); - - if (rte->rtekind != RTE_RELATION) - continue; - if (relid == rte->relid || relid == InvalidOid) - { - /* Invalidate the plan! */ - plan->dead = true; - break; /* out of rangetable scan */ - } - } - if (plan->dead) + /* Invalidate the plan! */ + plan->dead = true; break; /* out of stmt_list scan */ + } } } else diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 992b47f58d..9b6c637284 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.95 2007/09/20 17:56:32 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.96 2007/10/11 18:05:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -70,6 +70,8 @@ typedef struct PlannedStmt List *rowMarks; /* a list of RowMarkClause's */ + List *relationOids; /* OIDs of relations the plan depends on */ + int nParamExec; /* number of PARAM_EXEC Params used */ } PlannedStmt; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 32c699b6de..3890352022 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.146 2007/09/20 17:56:32 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.147 2007/10/11 18:05:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -72,6 +72,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *relationOids; /* OIDs of relations the plan depends on */ + bool transientPlan; /* redo plan when TransactionXmin changes? */ } PlannerGlobal; diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 4673d53098..43769c71e1 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.102 2007/10/04 20:44:47 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.103 2007/10/11 18:05:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -96,7 +96,8 @@ extern RestrictInfo *build_implied_join_equality(Oid opno, extern Plan *set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable); -extern List *set_returning_clause_references(List *rlist, +extern List *set_returning_clause_references(PlannerGlobal *glob, + List *rlist, Plan *topplan, Index resultRelation); extern void fix_opfuncids(Node *node); diff --git a/src/test/regress/expected/plancache.out b/src/test/regress/expected/plancache.out index a96ba2a5da..d7d7be9252 100644 --- a/src/test/regress/expected/plancache.out +++ b/src/test/regress/expected/plancache.out @@ -202,3 +202,20 @@ drop schema s1 cascade; NOTICE: drop cascades to table s1.abc drop schema s2 cascade; NOTICE: drop cascades to table abc +-- Check that invalidation deals with regclass constants +create temp sequence seq; +prepare p2 as select nextval('seq'); +execute p2; + nextval +--------- + 1 +(1 row) + +drop sequence seq; +create temp sequence seq; +execute p2; + nextval +--------- + 1 +(1 row) + diff --git a/src/test/regress/sql/plancache.sql b/src/test/regress/sql/plancache.sql index e285c071f6..fc57279d98 100644 --- a/src/test/regress/sql/plancache.sql +++ b/src/test/regress/sql/plancache.sql @@ -123,3 +123,17 @@ execute p1; drop schema s1 cascade; drop schema s2 cascade; + +-- Check that invalidation deals with regclass constants + +create temp sequence seq; + +prepare p2 as select nextval('seq'); + +execute p2; + +drop sequence seq; + +create temp sequence seq; + +execute p2; -- GitLab