|
65 | 65 | #include "utils/syscache.h"
|
66 | 66 |
|
67 | 67 |
|
| 68 | +/* |
| 69 | + * We must skip "overhead" operations that involve database access when the |
| 70 | + * cached plan's subject statement is a transaction control command. |
| 71 | + */ |
| 72 | +#define IsTransactionStmtPlan(plansource) \ |
| 73 | + ((plansource)->raw_parse_tree && \ |
| 74 | + IsA((plansource)->raw_parse_tree, TransactionStmt)) |
| 75 | + |
68 | 76 | /*
|
69 | 77 | * This is the head of the backend's list of "saved" CachedPlanSources (i.e.,
|
70 | 78 | * those that are in long-lived storage and are examined for sinval events).
|
@@ -352,9 +360,10 @@ CompleteCachedPlan(CachedPlanSource *plansource,
|
352 | 360 | /*
|
353 | 361 | * Use the planner machinery to extract dependencies. Data is saved in
|
354 | 362 | * query_context. (We assume that not a lot of extra cruft is created by
|
355 |
| - * this call.) We can skip this for one-shot plans. |
| 363 | + * this call.) We can skip this for one-shot plans, and transaction |
| 364 | + * control commands have no such dependencies anyway. |
356 | 365 | */
|
357 |
| - if (!plansource->is_oneshot) |
| 366 | + if (!plansource->is_oneshot && !IsTransactionStmtPlan(plansource)) |
358 | 367 | extract_query_dependencies((Node *) querytree_list,
|
359 | 368 | &plansource->relationOids,
|
360 | 369 | &plansource->invalItems);
|
@@ -384,9 +393,12 @@ CompleteCachedPlan(CachedPlanSource *plansource,
|
384 | 393 |
|
385 | 394 | /*
|
386 | 395 | * Fetch current search_path into dedicated context, but do any
|
387 |
| - * recalculation work required in caller's context. |
| 396 | + * recalculation work required in caller's context. We *must* skip this |
| 397 | + * for transaction control commands, because this could result in catalog |
| 398 | + * accesses. |
388 | 399 | */
|
389 |
| - plansource->search_path = GetOverrideSearchPath(source_context); |
| 400 | + if (!IsTransactionStmtPlan(plansource)) |
| 401 | + plansource->search_path = GetOverrideSearchPath(source_context); |
390 | 402 |
|
391 | 403 | plansource->is_complete = true;
|
392 | 404 | plansource->is_valid = true;
|
@@ -537,9 +549,11 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
|
537 | 549 | /*
|
538 | 550 | * For one-shot plans, we do not support revalidation checking; it's
|
539 | 551 | * assumed the query is parsed, planned, and executed in one transaction,
|
540 |
| - * so that no lock re-acquisition is necessary. |
| 552 | + * so that no lock re-acquisition is necessary. Also, there is never |
| 553 | + * any need to revalidate plans for transaction control commands (and |
| 554 | + * we mustn't risk any catalog accesses when handling those). |
541 | 555 | */
|
542 |
| - if (plansource->is_oneshot) |
| 556 | + if (plansource->is_oneshot || IsTransactionStmtPlan(plansource)) |
543 | 557 | {
|
544 | 558 | Assert(plansource->is_valid);
|
545 | 559 | return NIL;
|
@@ -859,7 +873,8 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
|
859 | 873 | * compared to parse analysis + planning, I'm not going to contort the
|
860 | 874 | * code enough to avoid that.
|
861 | 875 | */
|
862 |
| - PushOverrideSearchPath(plansource->search_path); |
| 876 | + if (plansource->search_path) |
| 877 | + PushOverrideSearchPath(plansource->search_path); |
863 | 878 |
|
864 | 879 | /*
|
865 | 880 | * If a snapshot is already set (the normal case), we can just use that
|
@@ -894,7 +909,8 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
|
894 | 909 | PopActiveSnapshot();
|
895 | 910 |
|
896 | 911 | /* Now we can restore current search path */
|
897 |
| - PopOverrideSearchPath(); |
| 912 | + if (plansource->search_path) |
| 913 | + PopOverrideSearchPath(); |
898 | 914 |
|
899 | 915 | /*
|
900 | 916 | * Normally we make a dedicated memory context for the CachedPlan and its
|
@@ -965,6 +981,9 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
|
965 | 981 | /* Otherwise, never any point in a custom plan if there's no parameters */
|
966 | 982 | if (boundParams == NULL)
|
967 | 983 | return false;
|
| 984 | + /* ... nor for transaction control statements */ |
| 985 | + if (IsTransactionStmtPlan(plansource)) |
| 986 | + return false; |
968 | 987 |
|
969 | 988 | /* See if caller wants to force the decision */
|
970 | 989 | if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN)
|
@@ -1267,7 +1286,8 @@ CopyCachedPlan(CachedPlanSource *plansource)
|
1267 | 1286 | newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
|
1268 | 1287 | else
|
1269 | 1288 | newsource->resultDesc = NULL;
|
1270 |
| - newsource->search_path = CopyOverrideSearchPath(plansource->search_path); |
| 1289 | + if (plansource->search_path) |
| 1290 | + newsource->search_path = CopyOverrideSearchPath(plansource->search_path); |
1271 | 1291 | newsource->context = source_context;
|
1272 | 1292 |
|
1273 | 1293 | querytree_context = AllocSetContextCreate(source_context,
|
@@ -1619,6 +1639,10 @@ PlanCacheRelCallback(Datum arg, Oid relid)
|
1619 | 1639 | if (!plansource->is_valid)
|
1620 | 1640 | continue;
|
1621 | 1641 |
|
| 1642 | + /* Never invalidate transaction control commands */ |
| 1643 | + if (IsTransactionStmtPlan(plansource)) |
| 1644 | + continue; |
| 1645 | + |
1622 | 1646 | /*
|
1623 | 1647 | * Check the dependency list for the rewritten querytree.
|
1624 | 1648 | */
|
@@ -1683,6 +1707,10 @@ PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue)
|
1683 | 1707 | if (!plansource->is_valid)
|
1684 | 1708 | continue;
|
1685 | 1709 |
|
| 1710 | + /* Never invalidate transaction control commands */ |
| 1711 | + if (IsTransactionStmtPlan(plansource)) |
| 1712 | + continue; |
| 1713 | + |
1686 | 1714 | /*
|
1687 | 1715 | * Check the dependency list for the rewritten querytree.
|
1688 | 1716 | */
|
@@ -1772,6 +1800,11 @@ ResetPlanCache(void)
|
1772 | 1800 | * We *must not* mark transaction control statements as invalid,
|
1773 | 1801 | * particularly not ROLLBACK, because they may need to be executed in
|
1774 | 1802 | * aborted transactions when we can't revalidate them (cf bug #5269).
|
| 1803 | + */ |
| 1804 | + if (IsTransactionStmtPlan(plansource)) |
| 1805 | + continue; |
| 1806 | + |
| 1807 | + /* |
1775 | 1808 | * In general there is no point in invalidating utility statements
|
1776 | 1809 | * since they have no plans anyway. So invalidate it only if it
|
1777 | 1810 | * contains at least one non-utility statement, or contains a utility
|
|
0 commit comments