Re: Tuple visibility within a single XID

From: Peter Geoghegan <pg(at)heroku(dot)com>
To: Jim Nasby <Jim(dot)Nasby(at)bluetreble(dot)com>
Cc: Pg Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Tuple visibility within a single XID
Date: 2015-04-08 01:11:35
Message-ID: CAM3SWZSrG6Pten9Gesn679+eeMtgjwJq3HiEZAocvEL4WjwpdA@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Tue, Apr 7, 2015 at 5:59 PM, Jim Nasby <Jim(dot)Nasby(at)bluetreble(dot)com> wrote:
> My understanding is that all subtransactions get their own unique XID
> (assuming they need one), and that CommandId can't move backwards within a
> transaction. If that's correct, then shouldn't we be able to prune tuples
> where XMIN and XMAX match our *exact* XID (not all the extra stuff that
> TransactionIdIsCurrentTransactionId() does) and CommandId <
> CurrentCommandId?

No. For one thing, unique index enforcement still requires the tuples
to be treated as a conflict while the other transaction is running
IMV.

For another, this is necessary today (from ExecUpdate()):

/*
* The target tuple was already updated or deleted by the
* current command, or by a later command in the current
* transaction. The former case is possible in a join UPDATE
* where multiple tuples join to the same target tuple. This
* is pretty questionable, but Postgres has always allowed it:
* we just execute the first update action and ignore
* additional update attempts.
*
* The latter case arises if the tuple is modified by a
* command in a BEFORE trigger, or perhaps by a command in a
* volatile function used in the query. In such situations we
* should not ignore the update, but it is equally unsafe to
* proceed. We don't want to discard the original UPDATE
* while keeping the triggered actions based on it; and we
* have no principled way to merge this update with the
* previous ones. So throwing an error is the only safe
* course.
*
* If a trigger actually intends this type of interaction, it
* can re-execute the UPDATE (assuming it can figure out how)
* and then return NULL to cancel the outer update.
*/
if (hufd.cmax != estate->es_output_cid)
ereport(ERROR,
(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
errmsg("tuple to be updated was already modified by an
operation triggered by the current command"),
errhint("Consider using an AFTER trigger instead of a
BEFORE trigger to propagate changes to other rows.")));

You're not the first to consider trying something like this in
specific scenarios, but my work on UPSERT leads me to believe it isn't
workable.
--
Peter Geoghegan

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Alvaro Herrera 2015-04-08 01:15:32 Re: Doubt about AccessExclusiveLock in ALTER TABLE .. SET ( .. );
Previous Message Jim Nasby 2015-04-08 00:59:26 Tuple visibility within a single XID