Skip to content

Commit e11b648

Browse files
committed
Fix misbehavior of CTE-used-in-a-subplan during EPQ rechecks.
An updating query that reads a CTE within an InitPlan or SubPlan could get incorrect results if it updates rows that are concurrently being modified. This is caused by CteScanNext supposing that nothing inside its recursive ExecProcNode call could change which read pointer is selected in the CTE's shared tuplestore. While that's normally true because of scoping considerations, it can break down if an EPQ plan tree gets built during the call, because EvalPlanQualStart builds execution trees for all subplans whether they're going to be used during the recheck or not. And it seems like a pretty shaky assumption anyway, so let's just reselect our own read pointer here. Per bug #14870 from Andrei Gorita. This has been broken since CTEs were implemented, so back-patch to all supported branches. Discussion: https://postgr.es/m/20171024155358.1471.82377@wrigleys.postgresql.org
1 parent 1bb87c0 commit e11b648

File tree

3 files changed

+37
-0
lines changed

3 files changed

+37
-0
lines changed

src/backend/executor/nodeCtescan.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ CteScanNext(CteScanState *node)
107107
return NULL;
108108
}
109109

110+
/*
111+
* There are corner cases where the subplan could change which
112+
* tuplestore read pointer is active, so be sure to reselect ours
113+
* before storing the tuple we got.
114+
*/
115+
tuplestore_select_read_pointer(tuplestorestate, node->readptr);
116+
110117
/*
111118
* Append a copy of the returned tuple to tuplestore. NOTE: because
112119
* our read pointer is certainly in EOF state, its read position will
@@ -176,6 +183,12 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
176183
* we might be asked to rescan the CTE even though upper levels didn't
177184
* tell us to be prepared to do it efficiently. Annoying, since this
178185
* prevents truncation of the tuplestore. XXX FIXME
186+
*
187+
* Note: if we are in an EPQ recheck plan tree, it's likely that no access
188+
* to the tuplestore is needed at all, making this even more annoying.
189+
* It's not worth improving that as long as all the read pointers would
190+
* have REWIND anyway, but if we ever improve this logic then that aspect
191+
* should be considered too.
179192
*/
180193
eflags |= EXEC_FLAG_REWIND;
181194

src/test/isolation/expected/eval-plan-qual.out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,18 @@ step readwcte: <... completed>
125125
id value
126126

127127
1 tableAValue2
128+
129+
starting permutation: wrtwcte multireadwcte c1 c2
130+
step wrtwcte: UPDATE table_a SET value = 'tableAValue2' WHERE id = 1;
131+
step multireadwcte:
132+
WITH updated AS (
133+
UPDATE table_a SET value = 'tableAValue3' WHERE id = 1 RETURNING id
134+
)
135+
SELECT (SELECT id FROM updated) AS subid, * FROM updated;
136+
<waiting ...>
137+
step c1: COMMIT;
138+
step c2: COMMIT;
139+
step multireadwcte: <... completed>
140+
subid id
141+
142+
1 1

src/test/isolation/specs/eval-plan-qual.spec

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ step "readwcte" {
9494
SELECT * FROM cte2;
9595
}
9696

97+
# this test exercises a different CTE misbehavior, cf bug #14870
98+
step "multireadwcte" {
99+
WITH updated AS (
100+
UPDATE table_a SET value = 'tableAValue3' WHERE id = 1 RETURNING id
101+
)
102+
SELECT (SELECT id FROM updated) AS subid, * FROM updated;
103+
}
104+
97105
teardown { COMMIT; }
98106

99107
permutation "wx1" "wx2" "c1" "c2" "read"
@@ -102,3 +110,4 @@ permutation "upsert1" "upsert2" "c1" "c2" "read"
102110
permutation "readp1" "writep1" "readp2" "c1" "c2"
103111
permutation "writep2" "returningp1" "c1" "c2"
104112
permutation "wrtwcte" "readwcte" "c1" "c2"
113+
permutation "wrtwcte" "multireadwcte" "c1" "c2"

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy