Skip to content

Commit dd9c6ff

Browse files
committed
Fix longstanding race condition in plancache.c.
When creating or manipulating a cached plan for a transaction control command (particularly ROLLBACK), we must not perform any catalog accesses, since we might be in an aborted transaction. However, plancache.c busily saved or examined the search_path for every cached plan. If we were unlucky enough to do this at a moment where the path's expansion into schema OIDs wasn't already cached, we'd do some catalog accesses; and with some more bad luck such as an ill-timed signal arrival, that could lead to crashes or Assert failures, as exhibited in bug #8095 from Nachiket Vaidya. Fortunately, there's no real need to consider the search path for such commands, so we can just skip the relevant steps when the subject statement is a TransactionStmt. This is somewhat related to bug #5269, though the failure happens during initial cached-plan creation rather than revalidation. This bug has been there since the plan cache was invented, so back-patch to all supported branches.
1 parent 8e00c48 commit dd9c6ff

File tree

1 file changed

+24
-6
lines changed

1 file changed

+24
-6
lines changed

src/backend/utils/cache/plancache.c

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@
6161
#include "utils/syscache.h"
6262

6363

64+
/*
65+
* We must skip "overhead" operations that involve database access when the
66+
* cached plan's subject statement is a transaction control command.
67+
*/
68+
#define IsTransactionStmtPlan(plansource) \
69+
((plansource)->raw_parse_tree && \
70+
IsA((plansource)->raw_parse_tree, TransactionStmt))
71+
6472
static List *cached_plans_list = NIL;
6573

6674
static void StoreCachedPlan(CachedPlanSource *plansource, List *stmt_list,
@@ -136,9 +144,13 @@ CreateCachedPlan(Node *raw_parse_tree,
136144

137145
/*
138146
* Fetch current search_path into new context, but do any recalculation
139-
* work required in caller's context.
147+
* work required in caller's context. Skip this for a transaction control
148+
* command, since we won't need it and can't risk catalog access.
140149
*/
141-
search_path = GetOverrideSearchPath(source_context);
150+
if (raw_parse_tree && IsA(raw_parse_tree, TransactionStmt))
151+
search_path = NULL;
152+
else
153+
search_path = GetOverrideSearchPath(source_context);
142154

143155
/*
144156
* Create and fill the CachedPlanSource struct within the new context.
@@ -229,9 +241,13 @@ FastCreateCachedPlan(Node *raw_parse_tree,
229241

230242
/*
231243
* Fetch current search_path into given context, but do any recalculation
232-
* work required in caller's context.
244+
* work required in caller's context. Skip this for a transaction control
245+
* command, since we won't need it and can't risk catalog access.
233246
*/
234-
search_path = GetOverrideSearchPath(context);
247+
if (raw_parse_tree && IsA(raw_parse_tree, TransactionStmt))
248+
search_path = NULL;
249+
else
250+
search_path = GetOverrideSearchPath(context);
235251

236252
/*
237253
* Create and fill the CachedPlanSource struct within the given context.
@@ -517,7 +533,8 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
517533
*
518534
* (XXX is there anything else we really need to restore?)
519535
*/
520-
PushOverrideSearchPath(plansource->search_path);
536+
if (plansource->search_path)
537+
PushOverrideSearchPath(plansource->search_path);
521538

522539
/*
523540
* If a snapshot is already set (the normal case), we can just use
@@ -601,7 +618,8 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
601618
PopActiveSnapshot();
602619

603620
/* Now we can restore current search path */
604-
PopOverrideSearchPath();
621+
if (plansource->search_path)
622+
PopOverrideSearchPath();
605623

606624
/*
607625
* Store the plans into the plancache entry, advancing the generation

0 commit comments

Comments
 (0)