diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/ri_triggers.c | 58 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 6 | ||||
-rw-r--r-- | src/backend/utils/time/tqual.c | 138 |
3 files changed, 107 insertions, 95 deletions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 9c32d57c111..20ad56c31f1 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -17,7 +17,7 @@ * * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.72 2004/09/10 18:40:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.73 2004/09/13 20:07:13 tgl Exp $ * * ---------- */ @@ -2698,16 +2698,20 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel) elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr); /* - * Run the plan. For safety we force a current query snapshot to be - * used. (In serializable mode, this arguably violates - * serializability, but we really haven't got much choice.) We need - * at most one tuple returned, so pass limit = 1. + * Run the plan. For safety we force a current snapshot to be used. + * (In serializable mode, this arguably violates serializability, but we + * really haven't got much choice.) We need at most one tuple returned, + * so pass limit = 1. */ - spi_result = SPI_execp_current(qplan, NULL, NULL, true, 1); + spi_result = SPI_execute_snapshot(qplan, + NULL, NULL, + CopySnapshot(GetLatestSnapshot()), + InvalidSnapshot, + true, 1); /* Check result */ if (spi_result != SPI_OK_SELECT) - elog(ERROR, "SPI_execp_current returned %d", spi_result); + elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); /* Did we find a tuple violating the constraint? */ if (SPI_processed > 0) @@ -3043,7 +3047,8 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan, Relation query_rel, source_rel; int key_idx; - bool useCurrentSnapshot; + Snapshot test_snapshot; + Snapshot crosscheck_snapshot; int limit; int spi_result; AclId save_uid; @@ -3094,21 +3099,26 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan, } /* - * In READ COMMITTED mode, we just need to make sure the regular query - * snapshot is up-to-date, and we will see all rows that could be - * interesting. In SERIALIZABLE mode, we can't update the regular - * query snapshot. If the caller passes detectNewRows == false then - * it's okay to do the query with the transaction snapshot; otherwise - * we tell the executor to force a current snapshot (and error out if - * it finds any rows under current snapshot that wouldn't be visible - * per the transaction snapshot). + * In READ COMMITTED mode, we just need to use an up-to-date regular + * snapshot, and we will see all rows that could be interesting. + * But in SERIALIZABLE mode, we can't change the transaction snapshot. + * If the caller passes detectNewRows == false then it's okay to do the + * query with the transaction snapshot; otherwise we use a current + * snapshot, and tell the executor to error out if it finds any rows under + * the current snapshot that wouldn't be visible per the transaction + * snapshot. */ - if (IsXactIsoLevelSerializable) - useCurrentSnapshot = detectNewRows; + if (IsXactIsoLevelSerializable && detectNewRows) + { + CommandCounterIncrement(); /* be sure all my own work is visible */ + test_snapshot = CopySnapshot(GetLatestSnapshot()); + crosscheck_snapshot = CopySnapshot(GetTransactionSnapshot()); + } else { - SetQuerySnapshot(); - useCurrentSnapshot = false; + /* the default SPI behavior is okay */ + test_snapshot = InvalidSnapshot; + crosscheck_snapshot = InvalidSnapshot; } /* @@ -3124,15 +3134,17 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan, SetUserId(RelationGetForm(query_rel)->relowner); /* Finally we can run the query. */ - spi_result = SPI_execp_current(qplan, vals, nulls, - useCurrentSnapshot, limit); + spi_result = SPI_execute_snapshot(qplan, + vals, nulls, + test_snapshot, crosscheck_snapshot, + false, limit); /* Restore UID */ SetUserId(save_uid); /* Check result */ if (spi_result < 0) - elog(ERROR, "SPI_execp_current returned %d", spi_result); + elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); if (expect_OK >= 0 && spi_result != expect_OK) ri_ReportViolation(qkey, constrname ? constrname : "", diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index af859222c36..89a9e2451dd 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.180 2004/09/01 23:58:38 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.181 2004/09/13 20:07:13 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -290,7 +290,7 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags) */ args[0] = ObjectIdGetDatum(ruleoid); nulls[0] = ' '; - spirc = SPI_execp(plan_getrulebyoid, args, nulls, 1); + spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 1); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid); if (SPI_processed != 1) @@ -425,7 +425,7 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags) args[1] = PointerGetDatum(ViewSelectRuleName); nulls[0] = ' '; nulls[1] = ' '; - spirc = SPI_execp(plan_getviewrule, args, nulls, 2); + spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 2); if (spirc != SPI_OK_SELECT) elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid); if (SPI_processed != 1) diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index ffdc4b9e6d9..5df7beaabdd 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -16,7 +16,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.77 2004/08/29 05:06:52 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.78 2004/09/13 20:07:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,18 +28,24 @@ #include "utils/tqual.h" /* - * The SnapshotData structs are static to simplify memory allocation + * These SnapshotData structs are static to simplify memory allocation * (see the hack in GetSnapshotData to avoid repeated malloc/free). */ -static SnapshotData QuerySnapshotData; -static SnapshotData SerializableSnapshotData; -static SnapshotData CurrentSnapshotData; static SnapshotData SnapshotDirtyData; +static SnapshotData SerializableSnapshotData; +static SnapshotData LatestSnapshotData; /* Externally visible pointers to valid snapshots: */ -Snapshot QuerySnapshot = NULL; -Snapshot SerializableSnapshot = NULL; Snapshot SnapshotDirty = &SnapshotDirtyData; +Snapshot SerializableSnapshot = NULL; +Snapshot LatestSnapshot = NULL; + +/* + * This pointer is not maintained by this module, but it's convenient + * to declare it here anyway. Callers typically assign a copy of + * GetTransactionSnapshot's result to ActiveSnapshot. + */ +Snapshot ActiveSnapshot = NULL; /* These are updated by GetSnapshotData: */ TransactionId RecentXmin = InvalidTransactionId; @@ -1028,101 +1034,94 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin) /* - * SetQuerySnapshot - * Initialize query snapshot for a new query + * GetTransactionSnapshot + * Get the appropriate snapshot for a new query in a transaction. * * The SerializableSnapshot is the first one taken in a transaction. * In serializable mode we just use that one throughout the transaction. - * In read-committed mode, we take a new snapshot at the start of each query. + * In read-committed mode, we take a new snapshot each time we are called. + * + * Note that the return value points at static storage that will be modified + * by future calls and by CommandCounterIncrement(). Callers should copy + * the result with CopySnapshot() if it is to be used very long. */ -void -SetQuerySnapshot(void) +Snapshot +GetTransactionSnapshot(void) { - /* 1st call in xaction? */ + /* First call in transaction? */ if (SerializableSnapshot == NULL) { SerializableSnapshot = GetSnapshotData(&SerializableSnapshotData, true); - QuerySnapshot = SerializableSnapshot; - Assert(QuerySnapshot != NULL); - return; + return SerializableSnapshot; } if (IsXactIsoLevelSerializable) - QuerySnapshot = SerializableSnapshot; - else - QuerySnapshot = GetSnapshotData(&QuerySnapshotData, false); + return SerializableSnapshot; - Assert(QuerySnapshot != NULL); + LatestSnapshot = GetSnapshotData(&LatestSnapshotData, false); + + return LatestSnapshot; } /* - * CopyQuerySnapshot - * Copy the current query snapshot. - * - * Copying the snapshot is done so that a query is guaranteed to use a - * consistent snapshot for its entire execution life, even if the command - * counter is incremented or SetQuerySnapshot() is called while it runs - * (as could easily happen, due to triggers etc. executing queries). - * - * The copy is palloc'd in the current memory context. + * GetLatestSnapshot + * Get a snapshot that is up-to-date as of the current instant, + * even if we are executing in SERIALIZABLE mode. */ Snapshot -CopyQuerySnapshot(void) +GetLatestSnapshot(void) { - Snapshot snapshot; - - if (QuerySnapshot == NULL) /* should be set beforehand */ + /* Should not be first call in transaction */ + if (SerializableSnapshot == NULL) elog(ERROR, "no snapshot has been set"); - snapshot = (Snapshot) palloc(sizeof(SnapshotData)); - memcpy(snapshot, QuerySnapshot, sizeof(SnapshotData)); - if (snapshot->xcnt > 0) - { - snapshot->xip = (TransactionId *) - palloc(snapshot->xcnt * sizeof(TransactionId)); - memcpy(snapshot->xip, QuerySnapshot->xip, - snapshot->xcnt * sizeof(TransactionId)); - } - else - snapshot->xip = NULL; + LatestSnapshot = GetSnapshotData(&LatestSnapshotData, false); - return snapshot; + return LatestSnapshot; } /* - * CopyCurrentSnapshot - * Make a snapshot that is up-to-date as of the current instant, - * and return a copy. + * CopySnapshot + * Copy the given snapshot. * * The copy is palloc'd in the current memory context. + * + * Note that this will not work on "special" snapshots. */ Snapshot -CopyCurrentSnapshot(void) +CopySnapshot(Snapshot snapshot) { - Snapshot currentSnapshot; - Snapshot snapshot; - - if (QuerySnapshot == NULL) /* should not be first call in xact */ - elog(ERROR, "no snapshot has been set"); - - /* Update the static struct */ - currentSnapshot = GetSnapshotData(&CurrentSnapshotData, false); - currentSnapshot->curcid = GetCurrentCommandId(); + Snapshot newsnap; - /* Make a copy */ - snapshot = (Snapshot) palloc(sizeof(SnapshotData)); - memcpy(snapshot, currentSnapshot, sizeof(SnapshotData)); + /* We allocate any XID array needed in the same palloc block. */ + newsnap = (Snapshot) palloc(sizeof(SnapshotData) + + snapshot->xcnt * sizeof(TransactionId)); + memcpy(newsnap, snapshot, sizeof(SnapshotData)); if (snapshot->xcnt > 0) { - snapshot->xip = (TransactionId *) - palloc(snapshot->xcnt * sizeof(TransactionId)); - memcpy(snapshot->xip, currentSnapshot->xip, + newsnap->xip = (TransactionId *) (newsnap + 1); + memcpy(newsnap->xip, snapshot->xip, snapshot->xcnt * sizeof(TransactionId)); } else - snapshot->xip = NULL; + newsnap->xip = NULL; - return snapshot; + return newsnap; +} + +/* + * FreeSnapshot + * Free a snapshot previously copied with CopySnapshot. + * + * This is currently identical to pfree, but is provided for cleanliness. + * + * Do *not* apply this to the results of GetTransactionSnapshot or + * GetLatestSnapshot. + */ +void +FreeSnapshot(Snapshot snapshot) +{ + pfree(snapshot); } /* @@ -1133,10 +1132,11 @@ void FreeXactSnapshot(void) { /* - * We do not free the xip arrays for the snapshot structs; they will - * be reused soon. So this is now just a state change to prevent + * We do not free the xip arrays for the static snapshot structs; they + * will be reused soon. So this is now just a state change to prevent * outside callers from accessing the snapshots. */ - QuerySnapshot = NULL; SerializableSnapshot = NULL; + LatestSnapshot = NULL; + ActiveSnapshot = NULL; /* just for cleanliness */ } |