1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-09 13:09:39 +03:00

Fix failure to ensure that a snapshot is available to datatype input functions

when they are invoked by the parser.  We had been setting up a snapshot at
plan time but really it needs to be done earlier, before parse analysis.
Per report from Dmitry Koterov.

Also fix two related problems discovered while poking at this one:
exec_bind_message called datatype input functions without establishing a
snapshot, and SET CONSTRAINTS IMMEDIATE could call trigger functions without
establishing a snapshot.

Backpatch to 8.2.  The underlying problem goes much further back, but it is
masked in 8.1 and before because we didn't attempt to invoke domain check
constraints within datatype input.  It would only be exposed if a C-language
datatype input function used the snapshot; which evidently none do, or we'd
have heard complaints sooner.  Since this code has changed a lot over time,
a back-patch is hardly risk-free, and so I'm disinclined to patch further
than absolutely necessary.
This commit is contained in:
Tom Lane
2008-12-13 02:00:30 +00:00
parent f6bab28488
commit 8d1d6019d4
5 changed files with 230 additions and 102 deletions

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.227.2.1 2008/10/25 03:32:44 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.227.2.2 2008/12/13 02:00:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -3469,19 +3469,51 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
if (!stmt->deferred)
{
AfterTriggerEventList *events = &afterTriggers->events;
Snapshot saveActiveSnapshot = ActiveSnapshot;
while (afterTriggerMarkEvents(events, NULL, true))
/* PG_TRY to ensure previous ActiveSnapshot is restored on error */
PG_TRY();
{
CommandId firing_id = afterTriggers->firing_counter++;
Snapshot mySnapshot = NULL;
/*
* We can delete fired events if we are at top transaction level,
* but we'd better not if inside a subtransaction, since the
* subtransaction could later get rolled back.
*/
afterTriggerInvokeEvents(-1, firing_id, NULL,
!IsSubTransaction());
while (afterTriggerMarkEvents(events, NULL, true))
{
CommandId firing_id = afterTriggers->firing_counter++;
/*
* Make sure a snapshot has been established in case trigger
* functions need one. Note that we avoid setting a snapshot
* if we don't find at least one trigger that has to be fired
* now. This is so that BEGIN; SET CONSTRAINTS ...; SET
* TRANSACTION ISOLATION LEVEL SERIALIZABLE; ... works
* properly. (If we are at the start of a transaction it's
* not possible for any trigger events to be queued yet.)
*/
if (mySnapshot == NULL)
{
mySnapshot = CopySnapshot(GetTransactionSnapshot());
ActiveSnapshot = mySnapshot;
}
/*
* We can delete fired events if we are at top transaction level,
* but we'd better not if inside a subtransaction, since the
* subtransaction could later get rolled back.
*/
afterTriggerInvokeEvents(-1, firing_id, NULL,
!IsSubTransaction());
}
if (mySnapshot)
FreeSnapshot(mySnapshot);
}
PG_CATCH();
{
ActiveSnapshot = saveActiveSnapshot;
PG_RE_THROW();
}
PG_END_TRY();
ActiveSnapshot = saveActiveSnapshot;
}
}