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:
parent
c06629c72e
commit
ee33b95d9c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue