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

Commitdeadbf4

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 parent2b53d58 commitdeadbf4

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
@@ -161,7 +161,29 @@ ExecLockRows(LockRowsState *node)
161161
*/
162162
if (!epq_started)
163163
{
164+
ListCell*lc2;
165+
164166
EvalPlanQualBegin(&node->lr_epqstate,estate);
167+
168+
/*
169+
* Ensure that rels with already-visited rowmarks are told
170+
* not to return tuples during the first EPQ test. We can
171+
* exit this loop once it reaches the current rowmark;
172+
* rels appearing later in the list will be set up
173+
* correctly by the EvalPlanQualSetTuple call at the top
174+
* of the loop.
175+
*/
176+
foreach(lc2,node->lr_arowMarks)
177+
{
178+
ExecAuxRowMark*aerm2= (ExecAuxRowMark*)lfirst(lc2);
179+
180+
if (lc2==lc)
181+
break;
182+
EvalPlanQualSetTuple(&node->lr_epqstate,
183+
aerm2->rowmark->rti,
184+
NULL);
185+
}
186+
165187
epq_started= true;
166188
}
167189

‎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