You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix a number of issues around modifying a previously updated row.
This commit fixes three, unfortunately related, issues:
1) Since 5db6df0, the introduction of DML via tableam, it was
possible to trigger "ERROR: unexpected table_lock_tuple status: 1"
when updating a row that was previously updated in the same
transaction - but only when the previously updated row was before
updated in a concurrent transaction (and READ COMMITTED was
used). The reason for that was that that case simply wasn't
expected. Fixing that lead to:
2) Even before the above commit, there were error checks (introduced
in 6868ed7) preventing a row being updated by different
commands within the same statement (say in a function called by an
UPDATE) - but that check wasn't performed when the row was first
updated in a concurrent transaction - instead the second update was
silently skipped in that case. After this change we throw the same
error as we'd without the concurrent transaction.
3) The error messages (introduced in 6868ed7) preventing such
updates emitted the same error message for both DELETE and
UPDATE ("tuple to be updated was already modified by an operation
triggered by the current command"). While that could be changed
separately, it made it hard to write tests that verify the correct
correct behavior of the code.
This commit changes heap's implementation of table_lock_tuple() to
return TM_SelfModified instead of TM_Invisible (previously loosely
modeled after EvalPlanQualFetch), and teaches nodeModifyTable.c to
handle that in response to table_lock_tuple() and not just in response
to table_(delete|update).
Additionally it fixes the wrong error message (see 3 above). The
comment for table_lock_tuple() is also adjusted to state that
TM_Deleted won't return information in TM_FailureData - it'll not
always be available.
This also adds tests to ensure that DELETE/UPDATE correctly error out
when affecting a row that concurrently was modified by another
transaction.
Author: Andres Freund
Reported-By: Tom Lane, when investigating a bug bug fix to another bug
by Amit Langote
Discussion: https://postgr.es/m/19321.1554567786@sss.pgh.pa.us
Copy file name to clipboardExpand all lines: src/test/isolation/expected/eval-plan-qual.out
+67
Original file line number
Diff line number
Diff line change
@@ -258,6 +258,73 @@ accountid balance
258
258
checking 1050
259
259
savings 600
260
260
261
+
starting permutation: wx1 updwcte c1 c2 read
262
+
step wx1: UPDATE accounts SET balance = balance - 200 WHERE accountid = 'checking' RETURNING balance;
263
+
balance
264
+
265
+
400
266
+
step updwcte: WITH doup AS (UPDATE accounts SET balance = balance + 1100 WHERE accountid = 'checking' RETURNING *) UPDATE accounts a SET balance = doup.balance + 100 FROM doup RETURNING *; <waiting ...>
267
+
step c1: COMMIT;
268
+
step updwcte: <... completed>
269
+
accountid balance accountid balance
270
+
271
+
savings 1600 checking 1500
272
+
step c2: COMMIT;
273
+
step read: SELECT * FROM accounts ORDER BY accountid;
274
+
accountid balance
275
+
276
+
checking 1500
277
+
savings 1600
278
+
279
+
starting permutation: wx1 updwctefail c1 c2 read
280
+
step wx1: UPDATE accounts SET balance = balance - 200 WHERE accountid = 'checking' RETURNING balance;
281
+
balance
282
+
283
+
400
284
+
step updwctefail: WITH doup AS (UPDATE accounts SET balance = balance + 1100 WHERE accountid = 'checking' RETURNING *, update_checking(999)) UPDATE accounts a SET balance = doup.balance + 100 FROM doup RETURNING *; <waiting ...>
285
+
step c1: COMMIT;
286
+
step updwctefail: <... completed>
287
+
error in steps c1 updwctefail: ERROR: tuple to be updated was already modified by an operation triggered by the current command
288
+
step c2: COMMIT;
289
+
step read: SELECT * FROM accounts ORDER BY accountid;
290
+
accountid balance
291
+
292
+
checking 400
293
+
savings 600
294
+
295
+
starting permutation: wx1 delwcte c1 c2 read
296
+
step wx1: UPDATE accounts SET balance = balance - 200 WHERE accountid = 'checking' RETURNING balance;
297
+
balance
298
+
299
+
400
300
+
step delwcte: WITH doup AS (UPDATE accounts SET balance = balance + 1100 WHERE accountid = 'checking' RETURNING *) DELETE FROM accounts a USING doup RETURNING *; <waiting ...>
301
+
step c1: COMMIT;
302
+
step delwcte: <... completed>
303
+
accountid balance accountid balance
304
+
305
+
savings 600 checking 1500
306
+
step c2: COMMIT;
307
+
step read: SELECT * FROM accounts ORDER BY accountid;
308
+
accountid balance
309
+
310
+
checking 1500
311
+
312
+
starting permutation: wx1 delwctefail c1 c2 read
313
+
step wx1: UPDATE accounts SET balance = balance - 200 WHERE accountid = 'checking' RETURNING balance;
314
+
balance
315
+
316
+
400
317
+
step delwctefail: WITH doup AS (UPDATE accounts SET balance = balance + 1100 WHERE accountid = 'checking' RETURNING *, update_checking(999)) DELETE FROM accounts a USING doup RETURNING *; <waiting ...>
318
+
step c1: COMMIT;
319
+
step delwctefail: <... completed>
320
+
error in steps c1 delwctefail: ERROR: tuple to be deleted was already modified by an operation triggered by the current command
321
+
step c2: COMMIT;
322
+
step read: SELECT * FROM accounts ORDER BY accountid;
0 commit comments