diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/ri_triggers.c | 55 | ||||
-rw-r--r-- | src/backend/utils/time/tqual.c | 60 |
2 files changed, 94 insertions, 21 deletions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 181484b7b2d..11b7e84df03 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-2003, PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.60 2003/09/29 00:05:25 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.61 2003/10/01 21:30:52 tgl Exp $ * * ---------- */ @@ -157,6 +157,7 @@ static void *ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, static bool ri_PerformCheck(RI_QueryKey *qkey, void *qplan, Relation fk_rel, Relation pk_rel, HeapTuple old_tuple, HeapTuple new_tuple, + bool detectNewRows, int expect_OK, const char *constrname); static void ri_ExtractValues(RI_QueryKey *qkey, int key_idx, Relation rel, HeapTuple tuple, @@ -276,6 +277,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, NULL, NULL, + false, SPI_OK_SELECT, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -433,6 +435,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, NULL, new_row, + false, SPI_OK_SELECT, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -594,6 +597,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, result = ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* treat like update */ SPI_OK_SELECT, NULL); if (SPI_finish() != SPI_OK_FINISH) @@ -752,6 +756,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_SELECT, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -942,6 +947,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_SELECT, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -1102,6 +1108,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_DELETE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -1285,6 +1292,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, new_row, + true, /* must detect new rows */ SPI_OK_UPDATE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -1453,6 +1461,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_SELECT, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -1633,6 +1642,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_SELECT, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -1802,6 +1812,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_UPDATE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -2019,6 +2030,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_UPDATE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -2188,6 +2200,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_UPDATE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -2392,6 +2405,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) ri_PerformCheck(&qkey, qplan, fk_rel, pk_rel, old_row, NULL, + true, /* must detect new rows */ SPI_OK_UPDATE, tgargs[RI_CONSTRAINT_NAME_ARGNO]); @@ -2788,11 +2802,13 @@ static bool ri_PerformCheck(RI_QueryKey *qkey, void *qplan, Relation fk_rel, Relation pk_rel, HeapTuple old_tuple, HeapTuple new_tuple, + bool detectNewRows, int expect_OK, const char *constrname) { Relation query_rel, source_rel; int key_idx; + bool useCurrentSnapshot; int limit; int spi_result; AclId save_uid; @@ -2842,9 +2858,25 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan, vals, nulls); } - /* Switch to proper UID to perform check as */ - save_uid = GetUserId(); - SetUserId(RelationGetForm(query_rel)->relowner); + /* + * 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). + */ + if (XactIsoLevel == XACT_SERIALIZABLE) + { + useCurrentSnapshot = detectNewRows; + } + else + { + SetQuerySnapshot(); + useCurrentSnapshot = false; + } /* * If this is a select query (e.g., for a 'no action' or 'restrict' @@ -2854,19 +2886,20 @@ ri_PerformCheck(RI_QueryKey *qkey, void *qplan, */ limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0; - /* - * Run the plan, using SnapshotNow time qual rules so that we can see - * all committed tuples, even those committed after our own transaction - * or query started. - */ - spi_result = SPI_execp_now(qplan, vals, nulls, limit); + /* Switch to proper UID to perform check as */ + save_uid = GetUserId(); + SetUserId(RelationGetForm(query_rel)->relowner); + + /* Finally we can run the query. */ + spi_result = SPI_execp_current(qplan, vals, nulls, + useCurrentSnapshot, limit); /* Restore UID */ SetUserId(save_uid); /* Check result */ if (spi_result < 0) - elog(ERROR, "SPI_execp_now returned %d", spi_result); + elog(ERROR, "SPI_execp_current returned %d", spi_result); if (expect_OK >= 0 && spi_result != expect_OK) ri_ReportViolation(qkey, constrname ? constrname : "", diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index 5b594fcf680..d7d22b77866 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 - * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.69 2003/09/25 18:58:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.70 2003/10/01 21:30:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -26,14 +26,19 @@ #include "storage/sinval.h" #include "utils/tqual.h" - -static SnapshotData SnapshotDirtyData; -Snapshot SnapshotDirty = &SnapshotDirtyData; - +/* + * The 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; + +/* Externally visible pointers to valid snapshots: */ Snapshot QuerySnapshot = NULL; Snapshot SerializableSnapshot = NULL; +Snapshot SnapshotDirty = &SnapshotDirtyData; /* These are updated by GetSnapshotData: */ TransactionId RecentXmin = InvalidTransactionId; @@ -387,10 +392,8 @@ HeapTupleSatisfiesToast(HeapTupleHeader tuple) * CurrentCommandId. */ int -HeapTupleSatisfiesUpdate(HeapTuple htuple, CommandId curcid) +HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid) { - HeapTupleHeader tuple = htuple->t_data; - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) @@ -1024,6 +1027,42 @@ CopyQuerySnapshot(void) } /* + * CopyCurrentSnapshot + * Make a snapshot that is up-to-date as of the current instant, + * and return a copy. + * + * The copy is palloc'd in the current memory context. + */ +Snapshot +CopyCurrentSnapshot(void) +{ + 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(); + + /* Make a copy */ + snapshot = (Snapshot) palloc(sizeof(SnapshotData)); + memcpy(snapshot, currentSnapshot, sizeof(SnapshotData)); + if (snapshot->xcnt > 0) + { + snapshot->xip = (TransactionId *) + palloc(snapshot->xcnt * sizeof(TransactionId)); + memcpy(snapshot->xip, currentSnapshot->xip, + snapshot->xcnt * sizeof(TransactionId)); + } + else + snapshot->xip = NULL; + + return snapshot; +} + +/* * FreeXactSnapshot * Free snapshot(s) at end of transaction. */ @@ -1031,8 +1070,9 @@ void FreeXactSnapshot(void) { /* - * We do not free(QuerySnapshot->xip); or - * free(SerializableSnapshot->xip); they will be reused soon + * 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 outside callers from accessing the snapshots. */ QuerySnapshot = NULL; SerializableSnapshot = NULL; |