From ee33b95d9c2ecec170bc517783d7268a4bd0c793 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 9 Sep 2008 18:58:09 +0000 Subject: [PATCH] Improve the plan cache invalidation mechanism to make it invalidate plans when user-defined functions used in a plan are modified. Also invalidate plans when schemas, operators, or operator classes are modified; but for these cases we just invalidate everything rather than tracking exact dependencies, since these types of objects seldom change in a production database. Tom Lane; loosely based on a patch by Martin Pihlak. --- src/backend/catalog/namespace.c | 6 +- src/backend/nodes/copyfuncs.c | 21 +- src/backend/nodes/outfuncs.c | 28 ++- src/backend/optimizer/plan/planner.c | 4 +- src/backend/optimizer/plan/setrefs.c | 285 ++++++++++++++++++--------- src/backend/optimizer/util/clauses.c | 24 ++- src/backend/parser/parse_oper.c | 6 +- src/backend/utils/adt/acl.c | 6 +- src/backend/utils/cache/inval.c | 84 ++++---- src/backend/utils/cache/plancache.c | 260 ++++++++++++++---------- src/backend/utils/cache/ts_cache.c | 15 +- src/backend/utils/misc/superuser.c | 6 +- src/include/nodes/nodes.h | 4 +- src/include/nodes/plannodes.h | 22 ++- src/include/nodes/relation.h | 4 +- src/include/optimizer/planmain.h | 6 +- src/include/utils/inval.h | 9 +- src/include/utils/plancache.h | 5 +- 18 files changed, 521 insertions(+), 274 deletions(-) diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index de827f74b2..f1763e5199 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.111 2008/09/01 20:42:43 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.112 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -187,7 +187,7 @@ static void recomputeNamespacePath(void); static void InitTempTableNamespace(void); static void RemoveTempRelations(Oid tempNamespaceId); static void RemoveTempRelationsCallback(int code, Datum arg); -static void NamespaceCallback(Datum arg, Oid relid); +static void NamespaceCallback(Datum arg, int cacheid, ItemPointer tuplePtr); /* These don't really need to appear in any header file */ Datum pg_table_is_visible(PG_FUNCTION_ARGS); @@ -3094,7 +3094,7 @@ InitializeSearchPath(void) * Syscache inval callback function */ static void -NamespaceCallback(Datum arg, Oid relid) +NamespaceCallback(Datum arg, int cacheid, ItemPointer tuplePtr) { /* Force search path to be recomputed on next use */ baseSearchPathValid = false; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 6e2028142b..9cfa1700f4 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.404 2008/09/01 20:42:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.405 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -88,6 +88,7 @@ _copyPlannedStmt(PlannedStmt *from) COPY_NODE_FIELD(returningLists); COPY_NODE_FIELD(rowMarks); COPY_NODE_FIELD(relationOids); + COPY_NODE_FIELD(invalItems); COPY_SCALAR_FIELD(nParamExec); return newnode; @@ -689,6 +690,21 @@ _copyLimit(Limit *from) return newnode; } +/* + * _copyPlanInvalItem + */ +static PlanInvalItem * +_copyPlanInvalItem(PlanInvalItem *from) +{ + PlanInvalItem *newnode = makeNode(PlanInvalItem); + + COPY_SCALAR_FIELD(cacheId); + /* tupleId isn't really a "scalar", but this works anyway */ + COPY_SCALAR_FIELD(tupleId); + + return newnode; +} + /* **************************************************************** * primnodes.h copy functions * **************************************************************** @@ -3157,6 +3173,9 @@ copyObject(void *from) case T_Limit: retval = _copyLimit(from); break; + case T_PlanInvalItem: + retval = _copyPlanInvalItem(from); + break; /* * PRIMITIVE NODES diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 15ced48445..0b74b3a063 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.338 2008/09/01 20:42:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.339 2008/09/09 18:58:08 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -255,6 +255,7 @@ _outPlannedStmt(StringInfo str, PlannedStmt *node) WRITE_NODE_FIELD(returningLists); WRITE_NODE_FIELD(rowMarks); WRITE_NODE_FIELD(relationOids); + WRITE_NODE_FIELD(invalItems); WRITE_INT_FIELD(nParamExec); } @@ -593,6 +594,14 @@ _outUnique(StringInfo str, Unique *node) appendStringInfo(str, " %u", node->uniqOperators[i]); } +static void +_outHash(StringInfo str, Hash *node) +{ + WRITE_NODE_TYPE("HASH"); + + _outPlanInfo(str, (Plan *) node); +} + static void _outSetOp(StringInfo str, SetOp *node) { @@ -631,11 +640,14 @@ _outLimit(StringInfo str, Limit *node) } static void -_outHash(StringInfo str, Hash *node) +_outPlanInvalItem(StringInfo str, PlanInvalItem *node) { - WRITE_NODE_TYPE("HASH"); + WRITE_NODE_TYPE("PLANINVALITEM"); - _outPlanInfo(str, (Plan *) node); + WRITE_INT_FIELD(cacheId); + appendStringInfo(str, " :tupleId (%u,%u)", + ItemPointerGetBlockNumber(&node->tupleId), + ItemPointerGetOffsetNumber(&node->tupleId)); } /***************************************************************************** @@ -1354,6 +1366,7 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node) WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); WRITE_NODE_FIELD(relationOids); + WRITE_NODE_FIELD(invalItems); } static void @@ -2206,14 +2219,17 @@ _outNode(StringInfo str, void *obj) case T_Unique: _outUnique(str, obj); break; + case T_Hash: + _outHash(str, obj); + break; case T_SetOp: _outSetOp(str, obj); break; case T_Limit: _outLimit(str, obj); break; - case T_Hash: - _outHash(str, obj); + case T_PlanInvalItem: + _outPlanInvalItem(str, obj); break; case T_Alias: _outAlias(str, obj); diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 40a659391c..ec2b0f794a 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.242 2008/08/17 01:19:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.243 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -140,6 +140,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; glob->relationOids = NIL; + glob->invalItems = NIL; glob->transientPlan = false; /* Determine what fraction of the plan is likely to be scanned */ @@ -213,6 +214,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) result->returningLists = root->returningLists; result->rowMarks = parse->rowMarks; result->relationOids = glob->relationOids; + result->invalItems = glob->invalItems; result->nParamExec = list_length(glob->paramlist); return result; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index b9d5643da8..1836262872 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,12 +9,13 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.143 2008/08/25 22:42:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.144 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/transam.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -23,6 +24,7 @@ #include "optimizer/tlist.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" +#include "utils/syscache.h" typedef struct @@ -112,6 +114,8 @@ static Node *fix_upper_expr(PlannerGlobal *glob, static Node *fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context); static bool fix_opfuncids_walker(Node *node, void *context); +static bool extract_query_dependencies_walker(Node *node, + PlannerGlobal *context); /***************************************************************************** @@ -138,10 +142,12 @@ 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. + * 5. We create lists of specific objects 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.) + * Relation dependencies are represented by OIDs, and everything else by + * PlanInvalItems (this distinction is motivated by the shared-inval APIs). + * Currently, relations and user-defined functions are the only types of + * objects that are explicitly tracked this way. * * We also perform one final optimization step, which is to delete * SubqueryScan plan nodes that aren't doing anything useful (ie, have @@ -164,7 +170,8 @@ static bool fix_opfuncids_walker(Node *node, void *context); * different when the passed-in Plan is a SubqueryScan we decide isn't needed. * * The flattened rangetable entries are appended to glob->finalrtable, and - * the list of relation OIDs is appended to glob->relationOids. + * plan dependencies are appended to glob->relationOids (for relations) + * and glob->invalItems (for everything else). * * 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 @@ -622,6 +629,74 @@ copyVar(Var *var) return newvar; } +/* + * fix_expr_common + * Do generic set_plan_references processing on an expression node + * + * This is code that is common to all variants of expression-fixing. + * We must look up operator opcode info for OpExpr and related nodes, + * add OIDs from regclass Const nodes into glob->relationOids, + * and add catalog TIDs for user-defined functions into glob->invalItems. + * + * We assume it's okay to update opcode info in-place. So this could possibly + * scribble on the planner's input data structures, but it's OK. + */ +static void +fix_expr_common(PlannerGlobal *glob, Node *node) +{ + /* We assume callers won't call us on a NULL pointer */ + if (IsA(node, Aggref)) + { + record_plan_function_dependency(glob, + ((Aggref *) node)->aggfnoid); + } + else if (IsA(node, FuncExpr)) + { + record_plan_function_dependency(glob, + ((FuncExpr *) node)->funcid); + } + else if (IsA(node, OpExpr)) + { + set_opfuncid((OpExpr *) node); + record_plan_function_dependency(glob, + ((OpExpr *) node)->opfuncid); + } + else if (IsA(node, DistinctExpr)) + { + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + record_plan_function_dependency(glob, + ((DistinctExpr *) node)->opfuncid); + } + else if (IsA(node, NullIfExpr)) + { + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + record_plan_function_dependency(glob, + ((NullIfExpr *) node)->opfuncid); + } + else if (IsA(node, ScalarArrayOpExpr)) + { + set_sa_opfuncid((ScalarArrayOpExpr *) node); + record_plan_function_dependency(glob, + ((ScalarArrayOpExpr *) node)->opfuncid); + } + else if (IsA(node, ArrayCoerceExpr)) + { + if (OidIsValid(((ArrayCoerceExpr *) node)->elemfuncid)) + record_plan_function_dependency(glob, + ((ArrayCoerceExpr *) node)->elemfuncid); + } + else if (IsA(node, Const)) + { + Const *con = (Const *) node; + + /* Check for regclass reference */ + if (ISREGCLASSCONST(con)) + glob->relationOids = + lappend_oid(glob->relationOids, + DatumGetObjectId(con->constvalue)); + } +} + /* * fix_scan_expr * Do set_plan_references processing on a scan-level expression @@ -687,30 +762,7 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context) cexpr->cvarno += context->rtoffset; return (Node *) cexpr; } - - /* - * Since we update opcode info in-place, this part could possibly scribble - * on the planner's input data structures, but it's OK. - */ - if (IsA(node, OpExpr)) - set_opfuncid((OpExpr *) node); - else if (IsA(node, DistinctExpr)) - set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, NullIfExpr)) - 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 (ISREGCLASSCONST(con)) - context->glob->relationOids = - lappend_oid(context->glob->relationOids, - DatumGetObjectId(con->constvalue)); - /* Fall through to let expression_tree_mutator copy it */ - } + fix_expr_common(context->glob, node); return expression_tree_mutator(node, fix_scan_expr_mutator, (void *) context); } @@ -720,25 +772,7 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *context) { if (node == NULL) return false; - if (IsA(node, OpExpr)) - set_opfuncid((OpExpr *) node); - else if (IsA(node, DistinctExpr)) - set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, NullIfExpr)) - 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 (ISREGCLASSCONST(con)) - context->glob->relationOids = - lappend_oid(context->glob->relationOids, - DatumGetObjectId(con->constvalue)); - return false; - } + fix_expr_common(context->glob, node); return expression_tree_walker(node, fix_scan_expr_walker, (void *) context); } @@ -1384,30 +1418,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) if (newvar) return (Node *) newvar; } - - /* - * Since we update opcode info in-place, this part could possibly scribble - * on the planner's input data structures, but it's OK. - */ - if (IsA(node, OpExpr)) - set_opfuncid((OpExpr *) node); - else if (IsA(node, DistinctExpr)) - set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, NullIfExpr)) - 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 (ISREGCLASSCONST(con)) - context->glob->relationOids = - lappend_oid(context->glob->relationOids, - DatumGetObjectId(con->constvalue)); - /* Fall through to let expression_tree_mutator copy it */ - } + fix_expr_common(context->glob, node); return expression_tree_mutator(node, fix_join_expr_mutator, (void *) context); @@ -1482,30 +1493,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) if (newvar) return (Node *) newvar; } - - /* - * Since we update opcode info in-place, this part could possibly scribble - * on the planner's input data structures, but it's OK. - */ - if (IsA(node, OpExpr)) - set_opfuncid((OpExpr *) node); - else if (IsA(node, DistinctExpr)) - set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ - else if (IsA(node, NullIfExpr)) - 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 (ISREGCLASSCONST(con)) - context->glob->relationOids = - lappend_oid(context->glob->relationOids, - DatumGetObjectId(con->constvalue)); - /* Fall through to let expression_tree_mutator copy it */ - } + fix_expr_common(context->glob, node); return expression_tree_mutator(node, fix_upper_expr_mutator, (void *) context); @@ -1624,3 +1612,108 @@ set_sa_opfuncid(ScalarArrayOpExpr *opexpr) if (opexpr->opfuncid == InvalidOid) opexpr->opfuncid = get_opcode(opexpr->opno); } + +/***************************************************************************** + * QUERY DEPENDENCY MANAGEMENT + *****************************************************************************/ + +/* + * record_plan_function_dependency + * Mark the current plan as depending on a particular function. + * + * This is exported so that the function-inlining code can record a + * dependency on a function that it's removed from the plan tree. + */ +void +record_plan_function_dependency(PlannerGlobal *glob, Oid funcid) +{ + /* + * For performance reasons, we don't bother to track built-in functions; + * we just assume they'll never change (or at least not in ways that'd + * invalidate plans using them). For this purpose we can consider a + * built-in function to be one with OID less than FirstBootstrapObjectId. + * Note that the OID generator guarantees never to generate such an + * OID after startup, even at OID wraparound. + */ + if (funcid >= (Oid) FirstBootstrapObjectId) + { + HeapTuple func_tuple; + PlanInvalItem *inval_item; + + func_tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(func_tuple)) + elog(ERROR, "cache lookup failed for function %u", funcid); + + inval_item = makeNode(PlanInvalItem); + + /* + * It would work to use any syscache on pg_proc, but plancache.c + * expects us to use PROCOID. + */ + inval_item->cacheId = PROCOID; + inval_item->tupleId = func_tuple->t_self; + + glob->invalItems = lappend(glob->invalItems, inval_item); + + ReleaseSysCache(func_tuple); + } +} + +/* + * extract_query_dependencies + * Given a list of not-yet-planned queries (i.e. Query nodes), + * extract their dependencies just as set_plan_references would do. + * + * This is needed by plancache.c to handle invalidation of cached unplanned + * queries. + */ +void +extract_query_dependencies(List *queries, + List **relationOids, + List **invalItems) +{ + PlannerGlobal glob; + + /* Make up a dummy PlannerGlobal so we can use this module's machinery */ + MemSet(&glob, 0, sizeof(glob)); + glob.type = T_PlannerGlobal; + glob.relationOids = NIL; + glob.invalItems = NIL; + + (void) extract_query_dependencies_walker((Node *) queries, &glob); + + *relationOids = glob.relationOids; + *invalItems = glob.invalItems; +} + +static bool +extract_query_dependencies_walker(Node *node, PlannerGlobal *context) +{ + if (node == NULL) + return false; + /* Extract function dependencies and check for regclass Consts */ + fix_expr_common(context, node); + if (IsA(node, Query)) + { + Query *query = (Query *) node; + ListCell *lc; + + /* Collect relation OIDs in this Query's rtable */ + foreach(lc, query->rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + + if (rte->rtekind == RTE_RELATION) + context->relationOids = lappend_oid(context->relationOids, + rte->relid); + } + + /* And recurse into the query's subexpressions */ + return query_tree_walker(query, extract_query_dependencies_walker, + (void *) context, 0); + } + return expression_tree_walker(node, extract_query_dependencies_walker, + (void *) context); +} diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 254b86e0d9..097105a89a 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.266 2008/08/28 23:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.267 2008/09/09 18:58:08 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -50,6 +50,7 @@ typedef struct { ParamListInfo boundParams; + PlannerGlobal *glob; List *active_fns; Node *case_val; bool estimate; @@ -1890,9 +1891,15 @@ eval_const_expressions(PlannerInfo *root, Node *node) eval_const_expressions_context context; if (root) + { context.boundParams = root->glob->boundParams; /* bound Params */ + context.glob = root->glob; /* for inlined-function dependencies */ + } else + { context.boundParams = NULL; + context.glob = NULL; + } context.active_fns = NIL; /* nothing being recursively simplified */ context.case_val = NULL; /* no CASE being examined */ context.estimate = false; /* safe transformations only */ @@ -1921,6 +1928,8 @@ estimate_expression_value(PlannerInfo *root, Node *node) eval_const_expressions_context context; context.boundParams = root->glob->boundParams; /* bound Params */ + /* we do not need to mark the plan as depending on inlined functions */ + context.glob = NULL; context.active_fns = NIL; /* nothing being recursively simplified */ context.case_val = NULL; /* no CASE being examined */ context.estimate = true; /* unsafe transformations OK */ @@ -3468,6 +3477,13 @@ inline_function(Oid funcid, Oid result_type, List *args, MemoryContextDelete(mycxt); + /* + * Since there is now no trace of the function in the plan tree, we + * must explicitly record the plan's dependency on the function. + */ + if (context->glob) + record_plan_function_dependency(context->glob, funcid); + /* * Recursively try to simplify the modified expression. Here we must add * the current function to the context list of active functions. @@ -3842,6 +3858,12 @@ inline_set_returning_function(PlannerInfo *root, Node *node) error_context_stack = sqlerrcontext.previous; ReleaseSysCache(func_tuple); + /* + * Since there is now no trace of the function in the plan tree, we + * must explicitly record the plan's dependency on the function. + */ + record_plan_function_dependency(root->glob, fexpr->funcid); + return querytree; /* Here if func is not inlinable: release temp memory and return NULL */ diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 0ff5b59d00..51114f7b46 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.105 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.106 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -79,7 +79,7 @@ static bool make_oper_cache_key(OprCacheKey *key, List *opname, Oid ltypeId, Oid rtypeId); static Oid find_oper_cache_entry(OprCacheKey *key); static void make_oper_cache_entry(OprCacheKey *key, Oid opr_oid); -static void InvalidateOprCacheCallBack(Datum arg, Oid relid); +static void InvalidateOprCacheCallBack(Datum arg, int cacheid, ItemPointer tuplePtr); /* @@ -1130,7 +1130,7 @@ make_oper_cache_entry(OprCacheKey *key, Oid opr_oid) * Callback for pg_operator and pg_cast inval events */ static void -InvalidateOprCacheCallBack(Datum arg, Oid relid) +InvalidateOprCacheCallBack(Datum arg, int cacheid, ItemPointer tuplePtr) { HASH_SEQ_STATUS status; OprCacheEntry *hentry; diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index d0d0775118..16d5f64fb0 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.141 2008/09/08 00:47:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.142 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,7 +92,7 @@ static AclMode convert_tablespace_priv_string(text *priv_type_text); static AclMode convert_role_priv_string(text *priv_type_text); static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode); -static void RoleMembershipCacheCallback(Datum arg, Oid relid); +static void RoleMembershipCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr); /* @@ -2822,7 +2822,7 @@ initialize_acl(void) * Syscache inval callback function */ static void -RoleMembershipCacheCallback(Datum arg, Oid relid) +RoleMembershipCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr) { /* Force membership caches to be recomputed on next use */ cached_privs_role = InvalidOid; diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 050d7cc88d..5c9451022d 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -80,7 +80,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.86 2008/06/19 21:32:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.87 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -160,16 +160,25 @@ static TransInvalidationInfo *transInvalInfo = NULL; * assumes there won't be very many of these at once; could improve if needed. */ -#define MAX_CACHE_CALLBACKS 20 +#define MAX_SYSCACHE_CALLBACKS 20 +#define MAX_RELCACHE_CALLBACKS 5 -static struct CACHECALLBACK +static struct SYSCACHECALLBACK { - int16 id; /* cache number or message type id */ - CacheCallbackFunction function; + int16 id; /* cache number */ + SyscacheCallbackFunction function; Datum arg; -} cache_callback_list[MAX_CACHE_CALLBACKS]; +} syscache_callback_list[MAX_SYSCACHE_CALLBACKS]; -static int cache_callback_count = 0; +static int syscache_callback_count = 0; + +static struct RELCACHECALLBACK +{ + RelcacheCallbackFunction function; + Datum arg; +} relcache_callback_list[MAX_RELCACHE_CALLBACKS]; + +static int relcache_callback_count = 0; /* info values for 2PC callback */ #define TWOPHASE_INFO_MSG 0 /* SharedInvalidationMessage */ @@ -484,12 +493,13 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) msg->cc.hashValue, &msg->cc.tuplePtr); - for (i = 0; i < cache_callback_count; i++) + for (i = 0; i < syscache_callback_count; i++) { - struct CACHECALLBACK *ccitem = cache_callback_list + i; + struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i; if (ccitem->id == msg->cc.id) - (*ccitem->function) (ccitem->arg, InvalidOid); + (*ccitem->function) (ccitem->arg, + msg->cc.id, &msg->cc.tuplePtr); } } } @@ -499,12 +509,11 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) { RelationCacheInvalidateEntry(msg->rc.relId); - for (i = 0; i < cache_callback_count; i++) + for (i = 0; i < relcache_callback_count; i++) { - struct CACHECALLBACK *ccitem = cache_callback_list + i; + struct RELCACHECALLBACK *ccitem = relcache_callback_list + i; - if (ccitem->id == SHAREDINVALRELCACHE_ID) - (*ccitem->function) (ccitem->arg, msg->rc.relId); + (*ccitem->function) (ccitem->arg, msg->rc.relId); } } } @@ -539,9 +548,16 @@ InvalidateSystemCaches(void) ResetCatalogCaches(); RelationCacheInvalidate(); /* gets smgr cache too */ - for (i = 0; i < cache_callback_count; i++) + for (i = 0; i < syscache_callback_count; i++) { - struct CACHECALLBACK *ccitem = cache_callback_list + i; + struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i; + + (*ccitem->function) (ccitem->arg, ccitem->id, NULL); + } + + for (i = 0; i < relcache_callback_count; i++) + { + struct RELCACHECALLBACK *ccitem = relcache_callback_list + i; (*ccitem->function) (ccitem->arg, InvalidOid); } @@ -1177,26 +1193,25 @@ CacheInvalidateRelcacheByRelid(Oid relid) /* * CacheRegisterSyscacheCallback * Register the specified function to be called for all future - * invalidation events in the specified cache. + * invalidation events in the specified cache. The cache ID and the + * TID of the tuple being invalidated will be passed to the function. * - * NOTE: currently, the OID argument to the callback routine is not - * provided for syscache callbacks; the routine doesn't really get any - * useful info as to exactly what changed. It should treat every call - * as a "cache flush" request. + * NOTE: NULL will be passed for the TID if a cache reset request is received. + * In this case the called routines should flush all cached state. */ void CacheRegisterSyscacheCallback(int cacheid, - CacheCallbackFunction func, + SyscacheCallbackFunction func, Datum arg) { - if (cache_callback_count >= MAX_CACHE_CALLBACKS) - elog(FATAL, "out of cache_callback_list slots"); + if (syscache_callback_count >= MAX_SYSCACHE_CALLBACKS) + elog(FATAL, "out of syscache_callback_list slots"); - cache_callback_list[cache_callback_count].id = cacheid; - cache_callback_list[cache_callback_count].function = func; - cache_callback_list[cache_callback_count].arg = arg; + syscache_callback_list[syscache_callback_count].id = cacheid; + syscache_callback_list[syscache_callback_count].function = func; + syscache_callback_list[syscache_callback_count].arg = arg; - ++cache_callback_count; + ++syscache_callback_count; } /* @@ -1209,15 +1224,14 @@ CacheRegisterSyscacheCallback(int cacheid, * In this case the called routines should flush all cached state. */ void -CacheRegisterRelcacheCallback(CacheCallbackFunction func, +CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, Datum arg) { - if (cache_callback_count >= MAX_CACHE_CALLBACKS) - elog(FATAL, "out of cache_callback_list slots"); + if (relcache_callback_count >= MAX_RELCACHE_CALLBACKS) + elog(FATAL, "out of relcache_callback_list slots"); - cache_callback_list[cache_callback_count].id = SHAREDINVALRELCACHE_ID; - cache_callback_list[cache_callback_count].function = func; - cache_callback_list[cache_callback_count].arg = arg; + relcache_callback_list[relcache_callback_count].function = func; + relcache_callback_list[relcache_callback_count].arg = arg; - ++cache_callback_count; + ++relcache_callback_count; } diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index c0dc3649ac..bf1adf16ec 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -12,7 +12,7 @@ * * The plan cache manager itself is principally responsible for tracking * whether cached plans should be invalidated because of schema changes in - * the tables they depend on. When (and if) the next demand for a cached + * the objects they depend on. When (and if) the next demand for a cached * plan occurs, the query will be replanned. Note that this could result * in an error, for example if a column referenced by the query is no * longer present. The creator of a cached plan can specify whether it @@ -20,20 +20,22 @@ * could happen with "SELECT *" for example) --- if so, it's up to the * caller to notice changes and cope with them. * - * Currently, we use only relcache invalidation events to invalidate plans. - * This means that changes such as modification of a function definition do - * not invalidate plans using the function. This is not 100% OK --- for - * example, changing a SQL function that's been inlined really ought to - * cause invalidation of the plan that it's been inlined into --- but the - * cost of tracking additional types of object seems much higher than the - * gain, so we're just ignoring them for now. + * Currently, we track exactly the dependencies of plans on relations and + * user-defined functions. On relcache invalidation events or pg_proc + * syscache invalidation events, we invalidate just those plans that depend + * on the particular object being modified. (Note: this scheme assumes + * that any table modification that requires replanning will generate a + * relcache inval event.) We also watch for inval events on certain other + * system catalogs, such as pg_namespace; but for them, our response is + * just to invalidate all plans. We expect updates on those catalogs to + * be infrequent enough that more-detailed tracking is not worth the effort. * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.20 2008/08/25 22:42:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.21 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,6 +46,7 @@ #include "catalog/namespace.h" #include "executor/executor.h" #include "nodes/nodeFuncs.h" +#include "optimizer/planmain.h" #include "storage/lmgr.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" @@ -52,19 +55,7 @@ #include "utils/memutils.h" #include "utils/resowner.h" #include "utils/snapmgr.h" - - -typedef struct -{ - void (*callback) (); - void *arg; -} ScanQueryWalkerContext; - -typedef struct -{ - Oid inval_relid; - CachedPlan *plan; -} InvalRelidContext; +#include "utils/syscache.h" static List *cached_plans_list = NIL; @@ -73,28 +64,28 @@ static void StoreCachedPlan(CachedPlanSource *plansource, List *stmt_list, MemoryContext plan_context); static void AcquireExecutorLocks(List *stmt_list, bool acquire); static void AcquirePlannerLocks(List *stmt_list, bool acquire); -static void LockRelid(Oid relid, LOCKMODE lockmode, void *arg); -static void UnlockRelid(Oid relid, LOCKMODE lockmode, void *arg); -static void ScanQueryForRelids(Query *parsetree, - void (*callback) (), - void *arg); -static bool ScanQueryWalker(Node *node, ScanQueryWalkerContext *context); +static void ScanQueryForLocks(Query *parsetree, bool acquire); +static bool ScanQueryWalker(Node *node, bool *acquire); static bool rowmark_member(List *rowMarks, int rt_index); static bool plan_list_is_transient(List *stmt_list); -static void PlanCacheCallback(Datum arg, Oid relid); -static void InvalRelid(Oid relid, LOCKMODE lockmode, - InvalRelidContext *context); +static void PlanCacheRelCallback(Datum arg, Oid relid); +static void PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr); +static void PlanCacheSysCallback(Datum arg, int cacheid, ItemPointer tuplePtr); /* * InitPlanCache: initialize module during InitPostgres. * - * All we need to do is hook into inval.c's callback list. + * All we need to do is hook into inval.c's callback lists. */ void InitPlanCache(void) { - CacheRegisterRelcacheCallback(PlanCacheCallback, (Datum) 0); + CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0); + CacheRegisterSyscacheCallback(PROCOID, PlanCacheFuncCallback, (Datum) 0); + CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0); + CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0); + CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0); } /* @@ -337,6 +328,18 @@ StoreCachedPlan(CachedPlanSource *plansource, plan->refcount = 1; /* for the parent's link */ plan->generation = ++(plansource->generation); plan->context = plan_context; + if (plansource->fully_planned) + { + /* Planner already extracted dependencies, we don't have to */ + plan->relationOids = plan->invalItems = NIL; + } + else + { + /* Use the planner machinery to extract dependencies */ + extract_query_dependencies(stmt_list, + &plan->relationOids, + &plan->invalItems); + } Assert(plansource->plan == NULL); plansource->plan = plan; @@ -432,8 +435,8 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner) plan->dead = true; /* - * By now, if any invalidation has happened, PlanCacheCallback will - * have marked the plan dead. + * By now, if any invalidation has happened, the inval callback + * functions will have marked the plan dead. */ if (plan->dead) { @@ -637,37 +640,15 @@ AcquirePlannerLocks(List *stmt_list, bool acquire) Query *query = (Query *) lfirst(lc); Assert(IsA(query, Query)); - if (acquire) - ScanQueryForRelids(query, LockRelid, NULL); - else - ScanQueryForRelids(query, UnlockRelid, NULL); + ScanQueryForLocks(query, acquire); } } /* - * ScanQueryForRelids callback functions for AcquirePlannerLocks - */ -static void -LockRelid(Oid relid, LOCKMODE lockmode, void *arg) -{ - LockRelationOid(relid, lockmode); -} - -static void -UnlockRelid(Oid relid, LOCKMODE lockmode, void *arg) -{ - UnlockRelationOid(relid, lockmode); -} - -/* - * ScanQueryForRelids: recursively scan one Query and apply the callback - * function to each relation OID found therein. The callback function - * takes the arguments relation OID, lockmode, pointer arg. + * ScanQueryForLocks: recursively scan one Query for AcquirePlannerLocks. */ static void -ScanQueryForRelids(Query *parsetree, - void (*callback) (), - void *arg) +ScanQueryForLocks(Query *parsetree, bool acquire) { ListCell *lc; int rt_index; @@ -685,27 +666,22 @@ ScanQueryForRelids(Query *parsetree, switch (rte->rtekind) { case RTE_RELATION: - - /* - * Determine the lock type required for this RTE. - */ + /* Acquire or release the appropriate type of lock */ if (rt_index == parsetree->resultRelation) lockmode = RowExclusiveLock; else if (rowmark_member(parsetree->rowMarks, rt_index)) lockmode = RowShareLock; else lockmode = AccessShareLock; - - (*callback) (rte->relid, lockmode, arg); + if (acquire) + LockRelationOid(rte->relid, lockmode); + else + UnlockRelationOid(rte->relid, lockmode); break; case RTE_SUBQUERY: - - /* - * The subquery RTE itself is all right, but we have to - * recurse to process the represented subquery. - */ - ScanQueryForRelids(rte->subquery, callback, arg); + /* Recurse into subquery-in-FROM */ + ScanQueryForLocks(rte->subquery, acquire); break; default: @@ -720,21 +696,17 @@ ScanQueryForRelids(Query *parsetree, */ if (parsetree->hasSubLinks) { - ScanQueryWalkerContext context; - - context.callback = callback; - context.arg = arg; query_tree_walker(parsetree, ScanQueryWalker, - (void *) &context, + (void *) &acquire, QTW_IGNORE_RT_SUBQUERIES); } } /* - * Walker to find sublink subqueries for ScanQueryForRelids + * Walker to find sublink subqueries for ScanQueryForLocks */ static bool -ScanQueryWalker(Node *node, ScanQueryWalkerContext *context) +ScanQueryWalker(Node *node, bool *acquire) { if (node == NULL) return false; @@ -743,17 +715,16 @@ ScanQueryWalker(Node *node, ScanQueryWalkerContext *context) SubLink *sub = (SubLink *) node; /* Do what we came for */ - ScanQueryForRelids((Query *) sub->subselect, - context->callback, context->arg); + ScanQueryForLocks((Query *) sub->subselect, *acquire); /* Fall through to process lefthand args of SubLink */ } /* - * Do NOT recurse into Query nodes, because ScanQueryForRelids already + * Do NOT recurse into Query nodes, because ScanQueryForLocks already * processed subselects of subselects for us. */ return expression_tree_walker(node, ScanQueryWalker, - (void *) context); + (void *) acquire); } /* @@ -863,17 +834,16 @@ PlanCacheComputeResultDesc(List *stmt_list) } /* - * PlanCacheCallback + * PlanCacheRelCallback * Relcache inval callback function * * Invalidate all plans mentioning the given rel, or all plans mentioning * any rel at all if relid == InvalidOid. */ static void -PlanCacheCallback(Datum arg, Oid relid) +PlanCacheRelCallback(Datum arg, Oid relid) { ListCell *lc1; - ListCell *lc2; foreach(lc1, cached_plans_list) { @@ -885,6 +855,9 @@ PlanCacheCallback(Datum arg, Oid relid) continue; if (plan->fully_planned) { + /* Have to check the per-PlannedStmt relid lists */ + ListCell *lc2; + foreach(lc2, plan->stmt_list) { PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2); @@ -903,42 +876,117 @@ PlanCacheCallback(Datum arg, Oid relid) } else { - /* - * For not-fully-planned entries we use ScanQueryForRelids, since - * a recursive traversal is needed. The callback API is a bit - * tedious but avoids duplication of coding. - */ - InvalRelidContext context; + /* Otherwise check the single list we built ourselves */ + if ((relid == InvalidOid) ? plan->relationOids != NIL : + list_member_oid(plan->relationOids, relid)) + plan->dead = true; + } + } +} - context.inval_relid = relid; - context.plan = plan; +/* + * PlanCacheFuncCallback + * Syscache inval callback function for PROCOID cache + * + * Invalidate all plans mentioning the given catalog entry, or all plans + * mentioning any member of this cache if tuplePtr == NULL. + * + * Note that the coding would support use for multiple caches, but right + * now only user-defined functions are tracked this way. + */ +static void +PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr) +{ + ListCell *lc1; + + foreach(lc1, cached_plans_list) + { + CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1); + CachedPlan *plan = plansource->plan; + + /* No work if it's already invalidated */ + if (!plan || plan->dead) + continue; + if (plan->fully_planned) + { + /* Have to check the per-PlannedStmt inval-item lists */ + ListCell *lc2; foreach(lc2, plan->stmt_list) { - Query *query = (Query *) lfirst(lc2); + PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2); + ListCell *lc3; + + Assert(!IsA(plannedstmt, Query)); + if (!IsA(plannedstmt, PlannedStmt)) + continue; /* Ignore utility statements */ + foreach(lc3, plannedstmt->invalItems) + { + PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3); + + if (item->cacheId != cacheid) + continue; + if (tuplePtr == NULL || + ItemPointerEquals(tuplePtr, &item->tupleId)) + { + /* Invalidate the plan! */ + plan->dead = true; + break; /* out of invalItems scan */ + } + } + if (plan->dead) + break; /* out of stmt_list scan */ + } + } + else + { + /* Otherwise check the single list we built ourselves */ + ListCell *lc2; + + foreach(lc2, plan->invalItems) + { + PlanInvalItem *item = (PlanInvalItem *) lfirst(lc2); - Assert(IsA(query, Query)); - ScanQueryForRelids(query, InvalRelid, (void *) &context); + if (item->cacheId != cacheid) + continue; + if (tuplePtr == NULL || + ItemPointerEquals(tuplePtr, &item->tupleId)) + { + /* Invalidate the plan! */ + plan->dead = true; + break; + } } } } } /* - * ResetPlanCache: drop all cached plans. + * PlanCacheSysCallback + * Syscache inval callback function for other caches + * + * Just invalidate everything... */ -void -ResetPlanCache(void) +static void +PlanCacheSysCallback(Datum arg, int cacheid, ItemPointer tuplePtr) { - PlanCacheCallback((Datum) 0, InvalidOid); + ResetPlanCache(); } /* - * ScanQueryForRelids callback function for PlanCacheCallback + * ResetPlanCache: drop all cached plans. */ -static void -InvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context) +void +ResetPlanCache(void) { - if (relid == context->inval_relid || context->inval_relid == InvalidOid) - context->plan->dead = true; + ListCell *lc; + + foreach(lc, cached_plans_list) + { + CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc); + CachedPlan *plan = plansource->plan; + + if (plan) + plan->dead = true; + } } diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c index 81ff577125..33b415ac6f 100644 --- a/src/backend/utils/cache/ts_cache.c +++ b/src/backend/utils/cache/ts_cache.c @@ -20,7 +20,7 @@ * Copyright (c) 2006-2008, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/ts_cache.c,v 1.7 2008/04/12 23:14:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/ts_cache.c,v 1.8 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -79,18 +79,19 @@ static Oid TSCurrentConfigCache = InvalidOid; /* - * We use this catcache callback to detect when a visible change to a TS + * We use this syscache callback to detect when a visible change to a TS * catalog entry has been made, by either our own backend or another one. - * We don't get enough information to know *which* specific catalog row - * changed, so we have to invalidate all related cache entries. Fortunately, - * it seems unlikely that TS configuration changes will occur often enough - * for this to be a performance problem. + * + * In principle we could just flush the specific cache entry that changed, + * but given that TS configuration changes are probably infrequent, it + * doesn't seem worth the trouble to determine that; we just flush all the + * entries of the related hash table. * * We can use the same function for all TS caches by passing the hash * table address as the "arg". */ static void -InvalidateTSCacheCallBack(Datum arg, Oid relid) +InvalidateTSCacheCallBack(Datum arg, int cacheid, ItemPointer tuplePtr) { HTAB *hash = (HTAB *) DatumGetPointer(arg); HASH_SEQ_STATUS status; diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c index fd6407626a..fdaa99e1f8 100644 --- a/src/backend/utils/misc/superuser.c +++ b/src/backend/utils/misc/superuser.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/superuser.c,v 1.37 2008/01/01 19:45:54 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/superuser.c,v 1.38 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,7 +36,7 @@ static Oid last_roleid = InvalidOid; /* InvalidOid == cache not valid */ static bool last_roleid_is_super = false; static bool roleid_callback_registered = false; -static void RoleidCallback(Datum arg, Oid relid); +static void RoleidCallback(Datum arg, int cacheid, ItemPointer tuplePtr); /* @@ -102,7 +102,7 @@ superuser_arg(Oid roleid) * Syscache inval callback function */ static void -RoleidCallback(Datum arg, Oid relid) +RoleidCallback(Datum arg, int cacheid, ItemPointer tuplePtr) { /* Invalidate our local cache in case role's superuserness changed */ last_roleid = InvalidOid; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 7e3d67a241..c02b4c1790 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.211 2008/08/30 01:39:14 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.212 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,6 +67,8 @@ typedef enum NodeTag T_Hash, T_SetOp, T_Limit, + /* this one isn't a subclass of Plan: */ + T_PlanInvalItem, /* * TAGS FOR PLAN STATE NODES (execnodes.h) diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index f0e0d08e03..2f0ed9a5d7 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.102 2008/08/07 19:35:02 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.103 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "access/sdir.h" #include "nodes/bitmapset.h" #include "nodes/primnodes.h" +#include "storage/itemptr.h" /* ---------------------------------------------------------------- @@ -72,6 +73,8 @@ typedef struct PlannedStmt List *relationOids; /* OIDs of relations the plan depends on */ + List *invalItems; /* other dependencies, as PlanInvalItems */ + int nParamExec; /* number of PARAM_EXEC Params used */ } PlannedStmt; @@ -559,4 +562,21 @@ typedef struct Limit Node *limitCount; /* COUNT parameter, or NULL if none */ } Limit; + +/* + * Plan invalidation info + * + * We track the objects on which a PlannedStmt depends in two ways: + * relations are recorded as a simple list of OIDs, and everything else + * is represented as a list of PlanInvalItems. A PlanInvalItem is designed + * to be used with the syscache invalidation mechanism, so it identifies a + * system catalog entry by cache ID and tuple TID. + */ +typedef struct PlanInvalItem +{ + NodeTag type; + int cacheId; /* a syscache ID, see utils/syscache.h */ + ItemPointerData tupleId; /* TID of the object's catalog tuple */ +} PlanInvalItem; + #endif /* PLANNODES_H */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 8fb6861e50..c66bc05a74 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.158 2008/08/14 18:48:00 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.159 2008/09/09 18:58:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -74,6 +74,8 @@ typedef struct PlannerGlobal List *relationOids; /* OIDs of relations the plan depends on */ + List *invalItems; /* other dependencies, as PlanInvalItems */ + bool transientPlan; /* redo plan when TransactionXmin changes? */ } PlannerGlobal; diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 1f15eda941..a6b15187ce 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.111 2008/08/14 18:48:00 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.112 2008/09/09 18:58:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -106,5 +106,9 @@ extern List *set_returning_clause_references(PlannerGlobal *glob, extern void fix_opfuncids(Node *node); extern void set_opfuncid(OpExpr *opexpr); extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr); +extern void record_plan_function_dependency(PlannerGlobal *glob, Oid funcid); +extern void extract_query_dependencies(List *queries, + List **relationOids, + List **invalItems); #endif /* PLANMAIN_H */ diff --git a/src/include/utils/inval.h b/src/include/utils/inval.h index 821c751d28..0060d59408 100644 --- a/src/include/utils/inval.h +++ b/src/include/utils/inval.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/inval.h,v 1.43 2008/06/19 00:46:06 alvherre Exp $ + * $PostgreSQL: pgsql/src/include/utils/inval.h,v 1.44 2008/09/09 18:58:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,7 +18,8 @@ #include "utils/relcache.h" -typedef void (*CacheCallbackFunction) (Datum arg, Oid relid); +typedef void (*SyscacheCallbackFunction) (Datum arg, int cacheid, ItemPointer tuplePtr); +typedef void (*RelcacheCallbackFunction) (Datum arg, Oid relid); extern void AcceptInvalidationMessages(void); @@ -50,10 +51,10 @@ extern void CacheInvalidateRelcacheByTuple(HeapTuple classTuple); extern void CacheInvalidateRelcacheByRelid(Oid relid); extern void CacheRegisterSyscacheCallback(int cacheid, - CacheCallbackFunction func, + SyscacheCallbackFunction func, Datum arg); -extern void CacheRegisterRelcacheCallback(CacheCallbackFunction func, +extern void CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, Datum arg); extern void inval_twophase_postcommit(TransactionId xid, uint16 info, diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h index 878ba7907a..e19da85e93 100644 --- a/src/include/utils/plancache.h +++ b/src/include/utils/plancache.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.12 2008/07/18 20:26:06 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.13 2008/09/09 18:58:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -79,6 +79,9 @@ typedef struct CachedPlan int refcount; /* count of live references to this struct */ int generation; /* counter, starting at 1, for replans */ MemoryContext context; /* context containing this CachedPlan */ + /* These fields are used only in the not-fully-planned case: */ + List *relationOids; /* OIDs of relations the stmts depend on */ + List *invalItems; /* other dependencies, as PlanInvalItems */ } CachedPlan; -- 2.40.0