diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2010-01-15 22:36:35 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2010-01-15 22:36:35 +0000 |
commit | 08f8d478ebc37e42f3ced07d17dae83d6a9a3810 (patch) | |
tree | aedef12bd96c3a789c72ae38e3cfab39801d4556 /src/backend/utils/cache | |
parent | 00b5ccebdd0d2925a2e5db0fdf067ea4b7bae799 (diff) | |
download | postgresql-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.c | 128 |
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; |