aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/cache
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2010-01-15 22:36:35 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2010-01-15 22:36:35 +0000
commit08f8d478ebc37e42f3ced07d17dae83d6a9a3810 (patch)
treeaedef12bd96c3a789c72ae38e3cfab39801d4556 /src/backend/utils/cache
parent00b5ccebdd0d2925a2e5db0fdf067ea4b7bae799 (diff)
downloadpostgresql-08f8d478ebc37e42f3ced07d17dae83d6a9a3810.tar.gz
postgresql-08f8d478ebc37e42f3ced07d17dae83d6a9a3810.zip
Do parse analysis of an EXPLAIN's contained statement during the normal
parse analysis phase, rather than at execution time. This makes parameter handling work the same as it does in ordinary plannable queries, and in particular fixes the incompatibility that Pavel pointed out with plpgsql's new handling of variable references. plancache.c gets a little bit grottier, but the alternatives seem worse.
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r--src/backend/utils/cache/plancache.c128
1 files changed, 90 insertions, 38 deletions
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index ffa117b66cd..114cd9b9756 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -35,7 +35,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.33 2010/01/13 16:56:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.34 2010/01/15 22:36:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -359,13 +359,27 @@ StoreCachedPlan(CachedPlanSource *plansource,
plan->context = plan_context;
if (plansource->fully_planned)
{
- /* Planner already extracted dependencies, we don't have to */
+ /*
+ * Planner already extracted dependencies, we don't have to ...
+ * except in the case of EXPLAIN. We assume here that EXPLAIN
+ * can't appear in a list with other commands.
+ */
plan->relationOids = plan->invalItems = NIL;
+
+ if (list_length(stmt_list) == 1 &&
+ IsA(linitial(stmt_list), ExplainStmt))
+ {
+ ExplainStmt *estmt = (ExplainStmt *) linitial(stmt_list);
+
+ extract_query_dependencies(estmt->query,
+ &plan->relationOids,
+ &plan->invalItems);
+ }
}
else
{
/* Use the planner machinery to extract dependencies */
- extract_query_dependencies(stmt_list,
+ extract_query_dependencies((Node *) stmt_list,
&plan->relationOids,
&plan->invalItems);
}
@@ -685,7 +699,24 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
Assert(!IsA(plannedstmt, Query));
if (!IsA(plannedstmt, PlannedStmt))
- continue; /* Ignore utility statements */
+ {
+ /*
+ * Ignore utility statements, except EXPLAIN which contains a
+ * parsed-but-not-planned query. Note: it's okay to use
+ * ScanQueryForLocks, even though the query hasn't been through
+ * rule rewriting, because rewriting doesn't change the query
+ * representation.
+ */
+ if (IsA(plannedstmt, ExplainStmt))
+ {
+ Query *query;
+
+ query = (Query *) ((ExplainStmt *) plannedstmt)->query;
+ Assert(IsA(query, Query));
+ ScanQueryForLocks(query, acquire);
+ }
+ continue;
+ }
rt_index = 0;
foreach(lc2, plannedstmt->rtable)
@@ -739,6 +770,19 @@ AcquirePlannerLocks(List *stmt_list, bool acquire)
Query *query = (Query *) lfirst(lc);
Assert(IsA(query, Query));
+
+ if (query->commandType == CMD_UTILITY)
+ {
+ /* Ignore utility statements, except EXPLAIN */
+ if (IsA(query->utilityStmt, ExplainStmt))
+ {
+ query = (Query *) ((ExplainStmt *) query->utilityStmt)->query;
+ Assert(IsA(query, Query));
+ ScanQueryForLocks(query, acquire);
+ }
+ continue;
+ }
+
ScanQueryForLocks(query, acquire);
}
}
@@ -752,6 +796,9 @@ ScanQueryForLocks(Query *parsetree, bool acquire)
ListCell *lc;
int rt_index;
+ /* Shouldn't get called on utility commands */
+ Assert(parsetree->commandType != CMD_UTILITY);
+
/*
* First, process RTEs of the current query level.
*/
@@ -942,7 +989,16 @@ PlanCacheRelCallback(Datum arg, Oid relid)
/* No work if it's already invalidated */
if (!plan || plan->dead)
continue;
- if (plan->fully_planned)
+
+ /*
+ * Check the list we built ourselves; this covers unplanned cases
+ * including EXPLAIN.
+ */
+ if ((relid == InvalidOid) ? plan->relationOids != NIL :
+ list_member_oid(plan->relationOids, relid))
+ plan->dead = true;
+
+ if (plan->fully_planned && !plan->dead)
{
/* Have to check the per-PlannedStmt relid lists */
ListCell *lc2;
@@ -963,13 +1019,6 @@ PlanCacheRelCallback(Datum arg, Oid relid)
}
}
}
- else
- {
- /* Otherwise check the single list we built ourselves */
- if ((relid == InvalidOid) ? plan->relationOids != NIL :
- list_member_oid(plan->relationOids, relid))
- plan->dead = true;
- }
}
}
@@ -992,15 +1041,34 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
CachedPlan *plan = plansource->plan;
+ ListCell *lc2;
/* No work if it's already invalidated */
if (!plan || plan->dead)
continue;
- if (plan->fully_planned)
+
+ /*
+ * Check the list we built ourselves; this covers unplanned cases
+ * including EXPLAIN.
+ */
+ foreach(lc2, plan->invalItems)
{
- /* Have to check the per-PlannedStmt inval-item lists */
- ListCell *lc2;
+ PlanInvalItem *item = (PlanInvalItem *) lfirst(lc2);
+ if (item->cacheId != cacheid)
+ continue;
+ if (tuplePtr == NULL ||
+ ItemPointerEquals(tuplePtr, &item->tupleId))
+ {
+ /* Invalidate the plan! */
+ plan->dead = true;
+ break;
+ }
+ }
+
+ if (plan->fully_planned && !plan->dead)
+ {
+ /* Have to check the per-PlannedStmt inval-item lists */
foreach(lc2, plan->stmt_list)
{
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
@@ -1027,26 +1095,6 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
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;
- }
- }
- }
}
}
@@ -1086,7 +1134,9 @@ ResetPlanCache(void)
* aborted transactions when we can't revalidate them (cf bug #5269).
* In general there is no point in invalidating utility statements
* since they have no plans anyway. So mark it dead only if it
- * contains at least one non-utility statement.
+ * contains at least one non-utility statement. (EXPLAIN counts as
+ * a non-utility statement, though, since it contains an analyzed
+ * query that might have dependencies.)
*/
if (plan->fully_planned)
{
@@ -1096,7 +1146,8 @@ ResetPlanCache(void)
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
Assert(!IsA(plannedstmt, Query));
- if (IsA(plannedstmt, PlannedStmt))
+ if (IsA(plannedstmt, PlannedStmt) ||
+ IsA(plannedstmt, ExplainStmt))
{
/* non-utility statement, so invalidate */
plan->dead = true;
@@ -1112,7 +1163,8 @@ ResetPlanCache(void)
Query *query = (Query *) lfirst(lc2);
Assert(IsA(query, Query));
- if (query->commandType != CMD_UTILITY)
+ if (query->commandType != CMD_UTILITY ||
+ IsA(query->utilityStmt, ExplainStmt))
{
/* non-utility statement, so invalidate */
plan->dead = true;