mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
Throw error if expiring tuple is again updated or deleted.
This prevents surprising behavior when a FOR EACH ROW trigger BEFORE UPDATE or BEFORE DELETE directly or indirectly updates or deletes the the old row. Prior to this patch the requested action on the row could be silently ignored while all triggered actions based on the occurence of the requested action could be committed. One example of how this could happen is if the BEFORE DELETE trigger for a "parent" row deleted "children" which had trigger functions to update summary or status data on the parent. This also prevents similar surprising problems if the query has a volatile function which updates a target row while it is already being updated. There are related issues present in FOR UPDATE cursors and READ COMMITTED queries which are not handled by this patch. These issues need further evalution to determine what change, if any, is needed. Where the new error messages are generated, in most cases the best fix will be to move code from the BEFORE trigger to an AFTER trigger. Where this is not feasible, the trigger can avoid the error by re-issuing the triggering statement and returning NULL. Documentation changes will be submitted in a separate patch. Kevin Grittner and Tom Lane with input from Florian Pflug and Robert Haas, based on problems encountered during conversion of Wisconsin Circuit Court trigger logic to plpgsql triggers.
This commit is contained in:
@@ -35,6 +35,29 @@ typedef enum
|
||||
LockTupleExclusive
|
||||
} LockTupleMode;
|
||||
|
||||
/*
|
||||
* When heap_update, heap_delete, or heap_lock_tuple fail because the target
|
||||
* tuple is already outdated, they fill in this struct to provide information
|
||||
* to the caller about what happened.
|
||||
* ctid is the target's ctid link: it is the same as the target's TID if the
|
||||
* target was deleted, or the location of the replacement tuple if the target
|
||||
* was updated.
|
||||
* xmax is the outdating transaction's XID. If the caller wants to visit the
|
||||
* replacement tuple, it must check that this matches before believing the
|
||||
* replacement is really a match.
|
||||
* cmax is the outdating command's CID, but only when the failure code is
|
||||
* HeapTupleSelfUpdated (i.e., something in the current transaction outdated
|
||||
* the tuple); otherwise cmax is zero. (We make this restriction because
|
||||
* HeapTupleHeaderGetCmax doesn't work for tuples outdated in other
|
||||
* transactions.)
|
||||
*/
|
||||
typedef struct HeapUpdateFailureData
|
||||
{
|
||||
ItemPointerData ctid;
|
||||
TransactionId xmax;
|
||||
CommandId cmax;
|
||||
} HeapUpdateFailureData;
|
||||
|
||||
|
||||
/* ----------------
|
||||
* function prototypes for heap access method
|
||||
@@ -100,16 +123,15 @@ extern Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid,
|
||||
extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
|
||||
CommandId cid, int options, BulkInsertState bistate);
|
||||
extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
|
||||
ItemPointer ctid, TransactionId *update_xmax,
|
||||
CommandId cid, Snapshot crosscheck, bool wait);
|
||||
CommandId cid, Snapshot crosscheck, bool wait,
|
||||
HeapUpdateFailureData *hufd);
|
||||
extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
|
||||
HeapTuple newtup,
|
||||
ItemPointer ctid, TransactionId *update_xmax,
|
||||
CommandId cid, Snapshot crosscheck, bool wait);
|
||||
CommandId cid, Snapshot crosscheck, bool wait,
|
||||
HeapUpdateFailureData *hufd);
|
||||
extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
|
||||
Buffer *buffer, ItemPointer ctid,
|
||||
TransactionId *update_xmax, CommandId cid,
|
||||
LockTupleMode mode, bool nowait);
|
||||
CommandId cid, LockTupleMode mode, bool nowait,
|
||||
Buffer *buffer, HeapUpdateFailureData *hufd);
|
||||
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
|
||||
extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid);
|
||||
extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
|
||||
|
Reference in New Issue
Block a user