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.
This commit is contained in:
Tom Lane 2008-09-09 18:58:09 +00:00
parent c06629c72e
commit ee33b95d9c
18 changed files with 522 additions and 275 deletions

View 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;

View 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

View 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);

View 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;

View 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);
}

View 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 */

View 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;

View 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;

View 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;
}

View 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
@ -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
* ScanQueryForLocks: recursively scan one Query 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.
*/
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(query, Query));
ScanQueryForRelids(query, InvalRelid, (void *) &context);
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);
if (item->cacheId != cacheid)
continue;
if (tuplePtr == NULL ||
ItemPointerEquals(tuplePtr, &item->tupleId))
{
/* Invalidate the plan! */
plan->dead = true;
break;
}
}
}
}
}
/*
* PlanCacheSysCallback
* Syscache inval callback function for other caches
*
* Just invalidate everything...
*/
static void
PlanCacheSysCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
{
ResetPlanCache();
}
/*
* ResetPlanCache: drop all cached plans.
*/
void
ResetPlanCache(void)
{
PlanCacheCallback((Datum) 0, InvalidOid);
}
ListCell *lc;
/*
* ScanQueryForRelids callback function for PlanCacheCallback
*/
static void
InvalRelid(Oid relid, LOCKMODE lockmode, InvalRelidContext *context)
{
if (relid == context->inval_relid || context->inval_relid == InvalidOid)
context->plan->dead = true;
foreach(lc, cached_plans_list)
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
CachedPlan *plan = plansource->plan;
if (plan)
plan->dead = true;
}
}

View 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;

View 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;

View 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)

View 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 */

View 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;

View 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 */

View 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,

View 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;