Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc37ec84

Browse files
committed
Fix longstanding race condition in plancache.c.
When creating or manipulating a cached plan for a transaction controlcommand (particularly ROLLBACK), we must not perform any catalog accesses,since we might be in an aborted transaction. However, plancache.c busilysaved or examined the search_path for every cached plan. If we wereunlucky enough to do this at a moment where the path's expansion intoschema OIDs wasn't already cached, we'd do some catalog accesses; and withsome more bad luck such as an ill-timed signal arrival, that could lead tocrashes or Assert failures, as exhibited in bug #8095 from Nachiket Vaidya.Fortunately, there's no real need to consider the search path for suchcommands, so we can just skip the relevant steps when the subject statementis a TransactionStmt. This is somewhat related to bug #5269, though thefailure happens during initial cached-plan creation rather thanrevalidation.This bug has been there since the plan cache was invented, so back-patchto all supported branches.
1 parente3a7675 commitc37ec84

File tree

1 file changed

+42
-9
lines changed

1 file changed

+42
-9
lines changed

‎src/backend/utils/cache/plancache.c

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@
6565
#include"utils/syscache.h"
6666

6767

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+
#defineIsTransactionStmtPlan(plansource) \
73+
((plansource)->raw_parse_tree && \
74+
IsA((plansource)->raw_parse_tree, TransactionStmt))
75+
6876
/*
6977
* This is the head of the backend's list of "saved" CachedPlanSources (i.e.,
7078
* those that are in long-lived storage and are examined for sinval events).
@@ -352,9 +360,10 @@ CompleteCachedPlan(CachedPlanSource *plansource,
352360
/*
353361
* Use the planner machinery to extract dependencies. Data is saved in
354362
* 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.
356365
*/
357-
if (!plansource->is_oneshot)
366+
if (!plansource->is_oneshot&& !IsTransactionStmtPlan(plansource))
358367
extract_query_dependencies((Node*)querytree_list,
359368
&plansource->relationOids,
360369
&plansource->invalItems);
@@ -384,9 +393,12 @@ CompleteCachedPlan(CachedPlanSource *plansource,
384393

385394
/*
386395
* 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.
388399
*/
389-
plansource->search_path=GetOverrideSearchPath(source_context);
400+
if (!IsTransactionStmtPlan(plansource))
401+
plansource->search_path=GetOverrideSearchPath(source_context);
390402

391403
plansource->is_complete= true;
392404
plansource->is_valid= true;
@@ -537,9 +549,11 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
537549
/*
538550
* For one-shot plans, we do not support revalidation checking; it's
539551
* 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).
541555
*/
542-
if (plansource->is_oneshot)
556+
if (plansource->is_oneshot||IsTransactionStmtPlan(plansource))
543557
{
544558
Assert(plansource->is_valid);
545559
returnNIL;
@@ -859,7 +873,8 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
859873
* compared to parse analysis + planning, I'm not going to contort the
860874
* code enough to avoid that.
861875
*/
862-
PushOverrideSearchPath(plansource->search_path);
876+
if (plansource->search_path)
877+
PushOverrideSearchPath(plansource->search_path);
863878

864879
/*
865880
* If a snapshot is already set (the normal case), we can just use that
@@ -894,7 +909,8 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
894909
PopActiveSnapshot();
895910

896911
/* Now we can restore current search path */
897-
PopOverrideSearchPath();
912+
if (plansource->search_path)
913+
PopOverrideSearchPath();
898914

899915
/*
900916
* Normally we make a dedicated memory context for the CachedPlan and its
@@ -965,6 +981,9 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
965981
/* Otherwise, never any point in a custom plan if there's no parameters */
966982
if (boundParams==NULL)
967983
return false;
984+
/* ... nor for transaction control statements */
985+
if (IsTransactionStmtPlan(plansource))
986+
return false;
968987

969988
/* See if caller wants to force the decision */
970989
if (plansource->cursor_options&CURSOR_OPT_GENERIC_PLAN)
@@ -1267,7 +1286,8 @@ CopyCachedPlan(CachedPlanSource *plansource)
12671286
newsource->resultDesc=CreateTupleDescCopy(plansource->resultDesc);
12681287
else
12691288
newsource->resultDesc=NULL;
1270-
newsource->search_path=CopyOverrideSearchPath(plansource->search_path);
1289+
if (plansource->search_path)
1290+
newsource->search_path=CopyOverrideSearchPath(plansource->search_path);
12711291
newsource->context=source_context;
12721292

12731293
querytree_context=AllocSetContextCreate(source_context,
@@ -1619,6 +1639,10 @@ PlanCacheRelCallback(Datum arg, Oid relid)
16191639
if (!plansource->is_valid)
16201640
continue;
16211641

1642+
/* Never invalidate transaction control commands */
1643+
if (IsTransactionStmtPlan(plansource))
1644+
continue;
1645+
16221646
/*
16231647
* Check the dependency list for the rewritten querytree.
16241648
*/
@@ -1683,6 +1707,10 @@ PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue)
16831707
if (!plansource->is_valid)
16841708
continue;
16851709

1710+
/* Never invalidate transaction control commands */
1711+
if (IsTransactionStmtPlan(plansource))
1712+
continue;
1713+
16861714
/*
16871715
* Check the dependency list for the rewritten querytree.
16881716
*/
@@ -1772,6 +1800,11 @@ ResetPlanCache(void)
17721800
* We *must not* mark transaction control statements as invalid,
17731801
* particularly not ROLLBACK, because they may need to be executed in
17741802
* aborted transactions when we can't revalidate them (cf bug #5269).
1803+
*/
1804+
if (IsTransactionStmtPlan(plansource))
1805+
continue;
1806+
1807+
/*
17751808
* In general there is no point in invalidating utility statements
17761809
* since they have no plans anyway. So invalidate it only if it
17771810
* contains at least one non-utility statement, or contains a utility

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp