]> granicus.if.org Git - postgresql/commitdiff
Improve the plan cache invalidation mechanism to make it invalidate plans
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 9 Sep 2008 18:58:09 +0000 (18:58 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 9 Sep 2008 18:58:09 +0000 (18:58 +0000)
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.

18 files changed:
src/backend/catalog/namespace.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_oper.c
src/backend/utils/adt/acl.c
src/backend/utils/cache/inval.c
src/backend/utils/cache/plancache.c
src/backend/utils/cache/ts_cache.c
src/backend/utils/misc/superuser.c
src/include/nodes/nodes.h
src/include/nodes/plannodes.h
src/include/nodes/relation.h
src/include/optimizer/planmain.h
src/include/utils/inval.h
src/include/utils/plancache.h

index de827f74b2fc492a205ad0f72310cab153d64d8c..f1763e5199c48fc858382e144ffded601e97699b 100644 (file)
@@ -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;
index 6e2028142b9514270ccfd38856ab1e2c6e87c280..9cfa1700f436e8d918ca1c4926b2a82f6f838d3c 100644 (file)
@@ -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
index 15ced48445af995cf4cd7990148de09d0a5fe9eb..0b74b3a063d4bdd7666322844e35bc53930f13fd 100644 (file)
@@ -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);
index 40a659391c6fddf25b58d95c5e4a48cec39b74aa..ec2b0f794a05b4424ae9f714399d6a3ceb012995 100644 (file)
@@ -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;
index b9d5643da8aa6c16007835380330205abdc9cb75..18362628727865b1fd75e614ee5ae1a11ff4d76a 100644 (file)
@@ -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);
+}
index 254b86e0d9676fd6743232abf97aa851b40950d4..097105a89a290b25448db343da39e15a2fa1c458 100644 (file)
@@ -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 */
index 0ff5b59d00fb769f54f4433bff17fd5c8901f6d1..51114f7b466604e2f92fa9f57bdf01afc6091e1a 100644 (file)
@@ -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;
index d0d07751188c6897e8992ed59a11ae1046a5f5e8..16d5f64fb091d7a7b664b1903ef48a04c2cbe8ca 100644 (file)
@@ -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;
index 050d7cc88de2a8eb4afff32ceaff49f9f0a5fdec..5c9451022d22325972c73899e24dca9e9c8ff942 100644 (file)
@@ -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;
 }
index c0dc3649ac0e308730b000059c636cb3fc9ecc1a..bf1adf16eccbead06896091fb18d15cec6fa5cc2 100644 (file)
@@ -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
  * 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"
 #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;
+       }
 }
index 81ff577125eb607d684f179024ec7358e6c4a88f..33b415ac6f09018af7a00c4a19ddcded151fc547 100644 (file)
@@ -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;
index fd6407626a0161e6a5f247d3bcd87d50d61f426c..fdaa99e1f81a8e4ba04ce2b8f32c844a619949bf 100644 (file)
@@ -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;
index 7e3d67a241fe9e05ab50f4670c017fe915b5816f..c02b4c17904b9a1970b6742298f7ca2b5d46b113 100644 (file)
@@ -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)
index f0e0d08e03ae0338dcb65b96cf136784cf070fd9..2f0ed9a5d793035b0c45b06a410bc454bc1de896 100644 (file)
@@ -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 */
index 8fb6861e50c7635643af0c80e2bfb1c93c571fd6..c66bc05a7497e4c2a5955558cf6248ccf345c6bf 100644 (file)
@@ -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;
 
index 1f15eda941acb3d57cd4979f23960763a98c4906..a6b15187cea8fe522434da787741d1534d273d34 100644 (file)
@@ -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 */
index 821c751d28019694a14c7147c5459877982d6039..0060d59408262bd713cb8845ab762247ce46ffd2 100644 (file)
@@ -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,
index 878ba7907a363d43d29f775c024ac002bae6651b..e19da85e93ac7013ff7788008edf1ecd694c3623 100644 (file)
@@ -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;