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

Commit2db576b

Browse files
committed
Fix corner case where SELECT FOR UPDATE could return a row twice.
In READ COMMITTED mode, if a SELECT FOR UPDATE discovers it has to redoWHERE-clause checking on rows that have been updated since the SELECT'ssnapshot, it invokes EvalPlanQual processing to do that. If this firstoccurs within a non-first child table of an inheritance tree, the previouscoding could accidentally re-return a matching row from an earlier,already-scanned child table. (And, to add insult to injury, I think thiscould make it miss returning a row that should have been returned, if theupdated row that this happens on should still have passed the WHERE qual.)Per report from Kyotaro Horiguchi; the added isolation test is based on histest case.This has been broken for quite awhile, so back-patch to all supportedbranches.
1 parent2646d2d commit2db576b

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

‎src/backend/executor/nodeLockRows.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,29 @@ ExecLockRows(LockRowsState *node)
194194
*/
195195
if (!epq_started)
196196
{
197+
ListCell*lc2;
198+
197199
EvalPlanQualBegin(&node->lr_epqstate,estate);
200+
201+
/*
202+
* Ensure that rels with already-visited rowmarks are told
203+
* not to return tuples during the first EPQ test. We can
204+
* exit this loop once it reaches the current rowmark;
205+
* rels appearing later in the list will be set up
206+
* correctly by the EvalPlanQualSetTuple call at the top
207+
* of the loop.
208+
*/
209+
foreach(lc2,node->lr_arowMarks)
210+
{
211+
ExecAuxRowMark*aerm2= (ExecAuxRowMark*)lfirst(lc2);
212+
213+
if (lc2==lc)
214+
break;
215+
EvalPlanQualSetTuple(&node->lr_epqstate,
216+
aerm2->rowmark->rti,
217+
NULL);
218+
}
219+
198220
epq_started= true;
199221
}
200222

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,26 @@ accountid balance
4949

5050
checking 600
5151
savings 2334
52+
53+
starting permutation: readp1 writep1 readp2 c1 c2
54+
step readp1: SELECT tableoid::regclass, ctid, * FROM p WHERE b IN (0, 1) AND c = 0 FOR UPDATE;
55+
tableoid ctid a b c
56+
57+
c1 (0,1) 0 0 0
58+
c1 (0,4) 0 1 0
59+
c2 (0,1) 1 0 0
60+
c2 (0,4) 1 1 0
61+
c3 (0,1) 2 0 0
62+
c3 (0,4) 2 1 0
63+
step writep1: UPDATE p SET b = -1 WHERE a = 1 AND b = 1 AND c = 0;
64+
step readp2: SELECT tableoid::regclass, ctid, * FROM p WHERE b IN (0, 1) AND c = 0 FOR UPDATE; <waiting ...>
65+
step c1: COMMIT;
66+
step readp2: <... completed>
67+
tableoid ctid a b c
68+
69+
c1 (0,1) 0 0 0
70+
c1 (0,4) 0 1 0
71+
c2 (0,1) 1 0 0
72+
c3 (0,1) 2 0 0
73+
c3 (0,4) 2 1 0
74+
step c2: COMMIT;

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,20 @@ setup
88
{
99
CREATETABLEaccounts (accountidtextPRIMARYKEY,balancenumericnotnull);
1010
INSERTINTOaccountsVALUES ('checking',600), ('savings',600);
11+
12+
CREATETABLEp (aint,bint,cint);
13+
CREATETABLEc1 ()INHERITS (p);
14+
CREATETABLEc2 ()INHERITS (p);
15+
CREATETABLEc3 ()INHERITS (p);
16+
INSERTINTOc1SELECT0,a/3,a%3FROMgenerate_series(0,9)a;
17+
INSERTINTOc2SELECT1,a/3,a%3FROMgenerate_series(0,9)a;
18+
INSERTINTOc3SELECT2,a/3,a%3FROMgenerate_series(0,9)a;
1119
}
1220

1321
teardown
1422
{
1523
DROPTABLEaccounts;
24+
DROPTABLEpCASCADE;
1625
}
1726

1827
session"s1"
@@ -30,6 +39,11 @@ step "upsert1"{
3039
INSERTINTOaccountsSELECT'savings',500
3140
WHERENOTEXISTS (SELECT1FROMupsert);
3241
}
42+
# tests with table p check inheritance cases, specifically a bug where
43+
# nodeLockRows did the wrong thing when the first updated tuple was in
44+
# a non-first child table
45+
step"readp1"{SELECTtableoid::regclass,ctid,*FROMpWHEREbIN (0,1)ANDc=0FORUPDATE; }
46+
step"writep1"{UPDATEpSETb=-1WHEREa=1ANDb=1ANDc=0; }
3347
step"c1"{COMMIT; }
3448

3549
session"s2"
@@ -44,6 +58,7 @@ step "upsert2"{
4458
INSERTINTOaccountsSELECT'savings',1234
4559
WHERENOTEXISTS (SELECT1FROMupsert);
4660
}
61+
step"readp2"{SELECTtableoid::regclass,ctid,*FROMpWHEREbIN (0,1)ANDc=0FORUPDATE; }
4762
step"c2"{COMMIT; }
4863

4964
session"s3"
@@ -54,3 +69,4 @@ teardown{ COMMIT; }
5469
permutation"wx1""wx2""c1""c2""read"
5570
permutation"wy1""wy2""c1""c2""read"
5671
permutation"upsert1""upsert2""c1""c2""read"
72+
permutation"readp1""writep1""readp2""c1""c2"

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp