]> granicus.if.org Git - postgresql/commit
Fix a number of issues around modifying a previously updated row.
authorAndres Freund <andres@anarazel.de>
Mon, 8 Apr 2019 05:14:47 +0000 (22:14 -0700)
committerAndres Freund <andres@anarazel.de>
Mon, 8 Apr 2019 05:14:47 +0000 (22:14 -0700)
commit41f5e04aec6cf63ba8392adf70e9289e9c3706d6
treebcf13643f57208f9dc8bbaf76957bbb5d350a68f
parent964bae4d8456e5406753027fa5a70181ddb4c835
Fix a number of issues around modifying a previously updated row.

This commit fixes three, unfortunately related, issues:

1) Since 5db6df0c01, 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 6868ed7491b7) 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 6868ed7491b7) 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
src/backend/access/heap/heapam_handler.c
src/backend/executor/nodeModifyTable.c
src/include/access/tableam.h
src/test/isolation/expected/eval-plan-qual.out
src/test/isolation/specs/eval-plan-qual.spec
src/test/regress/expected/triggers.out