to allow es_snapshot to be set to SnapshotNow rather than a query snapshot.
This solves a bug reported by Wade Klaver, wherein triggers fired as a
result of RI cascade updates could misbehave.
gettimeofday(&starttime, NULL);
/* call ExecutorStart to prepare the plan for execution */
- ExecutorStart(queryDesc, !stmt->analyze);
+ ExecutorStart(queryDesc, false, !stmt->analyze);
/* Execute the plan for statistics if asked for */
if (stmt->analyze)
if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple)
heap_freetuple(rettuple);
- /*
- * Might have been a referential integrity constraint trigger. Reset
- * the snapshot overriding flag.
- */
- ReferentialIntegritySnapshotOverride = false;
-
/*
* Release buffers
*/
* field of the QueryDesc is filled in to describe the tuples that will be
* returned, and the internal fields (estate and planstate) are set up.
*
+ * If useSnapshotNow is true, run the query with SnapshotNow time qual rules
+ * instead of the normal use of QuerySnapshot.
+ *
* If explainOnly is true, we are not actually intending to run the plan,
* only to set up for EXPLAIN; so skip unwanted side-effects.
*
* ----------------------------------------------------------------
*/
void
-ExecutorStart(QueryDesc *queryDesc, bool explainOnly)
+ExecutorStart(QueryDesc *queryDesc, bool useSnapshotNow, bool explainOnly)
{
EState *estate;
MemoryContext oldcontext;
* the life of this query, even if it outlives the current command and
* current snapshot.
*/
- estate->es_snapshot = CopyQuerySnapshot();
+ if (useSnapshotNow)
+ {
+ estate->es_snapshot = SnapshotNow;
+ estate->es_snapshot_cid = GetCurrentCommandId();
+ }
+ else
+ {
+ estate->es_snapshot = CopyQuerySnapshot();
+ estate->es_snapshot_cid = estate->es_snapshot->curcid;
+ }
/*
* Initialize the plan state tree
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
test = heap_mark4update(erm->relation, &tuple, &buffer,
- estate->es_snapshot->curcid);
+ estate->es_snapshot_cid);
ReleaseBuffer(buffer);
switch (test)
{
if (estate->es_into_relation_descriptor != NULL)
{
heap_insert(estate->es_into_relation_descriptor, tuple,
- estate->es_snapshot->curcid);
+ estate->es_snapshot_cid);
IncrAppended();
}
* insert the tuple
*/
newId = heap_insert(resultRelationDesc, tuple,
- estate->es_snapshot->curcid);
+ estate->es_snapshot_cid);
IncrAppended();
(estate->es_processed)++;
bool dodelete;
dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid,
- estate->es_snapshot->curcid);
+ estate->es_snapshot_cid);
if (!dodelete) /* "do nothing" */
return;
ldelete:;
result = heap_delete(resultRelationDesc, tupleid,
&ctid,
- estate->es_snapshot->curcid,
+ estate->es_snapshot_cid,
true /* wait for commit */);
switch (result)
{
newtuple = ExecBRUpdateTriggers(estate, resultRelInfo,
tupleid, tuple,
- estate->es_snapshot->curcid);
+ estate->es_snapshot_cid);
if (newtuple == NULL) /* "do nothing" */
return;
*/
result = heap_update(resultRelationDesc, tupleid, tuple,
&ctid,
- estate->es_snapshot->curcid,
+ estate->es_snapshot_cid,
true /* wait for commit */);
switch (result)
{
*/
epqstate->es_direction = ForwardScanDirection;
epqstate->es_snapshot = estate->es_snapshot;
+ epqstate->es_snapshot_cid = estate->es_snapshot_cid;
epqstate->es_range_table = estate->es_range_table;
epqstate->es_result_relations = estate->es_result_relations;
epqstate->es_num_result_relations = estate->es_num_result_relations;
*/
estate->es_direction = ForwardScanDirection;
estate->es_snapshot = SnapshotNow;
+ estate->es_snapshot_cid = FirstCommandId;
estate->es_range_table = NIL;
estate->es_result_relations = NULL;
/* Utility commands don't need Executor. */
if (es->qd->operation != CMD_UTILITY)
- ExecutorStart(es->qd, false);
+ ExecutorStart(es->qd, false, false);
es->status = F_EXEC_RUN;
}
sp_estate->es_tupleTable =
ExecCreateTupleTable(ExecCountSlotsNode(subplan->plan) + 10);
sp_estate->es_snapshot = estate->es_snapshot;
+ sp_estate->es_snapshot_cid = estate->es_snapshot_cid;
sp_estate->es_instrument = estate->es_instrument;
/*
sp_estate->es_tupleTable =
ExecCreateTupleTable(ExecCountSlotsNode(node->subplan) + 10);
sp_estate->es_snapshot = estate->es_snapshot;
+ sp_estate->es_snapshot_cid = estate->es_snapshot_cid;
sp_estate->es_instrument = estate->es_instrument;
/*
static int _SPI_curid = -1;
static int _SPI_execute(const char *src, int tcount, _SPI_plan *plan);
-static int _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount);
+static int _SPI_pquery(QueryDesc *queryDesc, bool runit,
+ bool useSnapshotNow, int tcount);
static int _SPI_execute_plan(_SPI_plan *plan,
- Datum *Values, const char *Nulls, int tcount);
+ Datum *Values, const char *Nulls,
+ bool useSnapshotNow, int tcount);
static void _SPI_cursor_operation(Portal portal, bool forward, int count,
DestReceiver *dest);
if (res < 0)
return res;
- res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
+ res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, false, tcount);
+
+ _SPI_end_call(true);
+ return res;
+}
+
+/*
+ * SPI_execp_now -- identical to SPI_execp, except that we use SnapshotNow
+ * instead of the normal QuerySnapshot. This is currently not documented
+ * in spi.sgml because it is only intended for use by RI triggers.
+ */
+int
+SPI_execp_now(void *plan, Datum *Values, const char *Nulls, int tcount)
+{
+ int res;
+
+ if (plan == NULL || tcount < 0)
+ return SPI_ERROR_ARGUMENT;
+
+ if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
+ return SPI_ERROR_PARAM;
+
+ res = _SPI_begin_call(true);
+ if (res < 0)
+ return res;
+
+ res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, true, tcount);
_SPI_end_call(true);
return res;
{
qdesc = CreateQueryDesc(queryTree, planTree, dest,
NULL, false);
- res = _SPI_pquery(qdesc, true,
+ res = _SPI_pquery(qdesc, true, false,
queryTree->canSetTag ? tcount : 0);
if (res < 0)
return res;
{
qdesc = CreateQueryDesc(queryTree, planTree, dest,
NULL, false);
- res = _SPI_pquery(qdesc, false, 0);
+ res = _SPI_pquery(qdesc, false, false, 0);
if (res < 0)
return res;
}
static int
_SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
- int tcount)
+ bool useSnapshotNow, int tcount)
{
List *query_list_list = plan->qtlist;
List *plan_list = plan->ptlist;
{
qdesc = CreateQueryDesc(queryTree, planTree, dest,
paramLI, false);
- res = _SPI_pquery(qdesc, true,
+ res = _SPI_pquery(qdesc, true, useSnapshotNow,
queryTree->canSetTag ? tcount : 0);
if (res < 0)
return res;
}
static int
-_SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
+_SPI_pquery(QueryDesc *queryDesc, bool runit, bool useSnapshotNow, int tcount)
{
int operation = queryDesc->operation;
int res;
ResetUsage();
#endif
- ExecutorStart(queryDesc, false);
+ ExecutorStart(queryDesc, useSnapshotNow, false);
ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
/*
* Call ExecStart to prepare the plan for execution
*/
- ExecutorStart(queryDesc, false);
+ ExecutorStart(queryDesc, false, false);
/*
* Run the plan to completion.
/*
* Call ExecStart to prepare the plan for execution
*/
- ExecutorStart(queryDesc, false);
+ ExecutorStart(queryDesc, false, false);
/*
* This tells PortalCleanup to shut down the executor
int i;
int match_type;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
int i;
int match_type;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
int i;
int match_type;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
void *qplan;
int i;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
int i;
int j;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
void *qplan;
int i;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
void *qplan;
int i;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
void *qplan;
int i;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
int match_type;
bool use_cached_query;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
RI_QueryKey qkey;
void *qplan;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
void *qplan;
int match_type;
- ReferentialIntegritySnapshotOverride = true;
-
/*
* Check that this is a valid trigger call on the right time and
* event.
*/
limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
- /* Run the plan */
- spi_result = SPI_execp(qplan, vals, nulls, limit);
+ /*
+ * 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);
/* Restore UID */
SetUserId(save_uid);
/* Check result */
if (spi_result < 0)
- elog(ERROR, "SPI_execp failed");
+ elog(ERROR, "SPI_execp_now returned %d", spi_result);
if (expect_OK >= 0 && spi_result != expect_OK)
ri_ReportViolation(qkey, constrname ? constrname : "",
TransactionId RecentXmin = InvalidTransactionId;
TransactionId RecentGlobalXmin = InvalidTransactionId;
-bool ReferentialIntegritySnapshotOverride = false;
-
/*
* HeapTupleSatisfiesItself
bool
HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
{
- /* XXX this is horribly ugly: */
- if (ReferentialIntegritySnapshotOverride)
- return HeapTupleSatisfiesNow(tuple);
-
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
{
if (tuple->t_infomask & HEAP_XMIN_INVALID)
void
SetQuerySnapshot(void)
{
- /* Initialize snapshot overriding to false */
- ReferentialIntegritySnapshotOverride = false;
-
/* 1st call in xaction? */
if (SerializableSnapshot == NULL)
{
relation, \
buffer, \
disk_page, \
- seeself, \
+ snapshot, \
nKeys, \
key, \
res) \
{ \
uint16 _infomask = (tuple)->t_data->t_infomask; \
\
- (res) = HeapTupleSatisfiesVisibility((tuple), (seeself)); \
+ (res) = HeapTupleSatisfiesVisibility((tuple), (snapshot)); \
if ((tuple)->t_data->t_infomask != _infomask) \
SetBufferCommitInfoNeedsSave(buffer); \
} \
/*
* prototypes from functions in execMain.c
*/
-extern void ExecutorStart(QueryDesc *queryDesc, bool explainOnly);
+extern void ExecutorStart(QueryDesc *queryDesc, bool useSnapshotNow,
+ bool explainOnly);
extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
ScanDirection direction, long count);
extern void ExecutorEnd(QueryDesc *queryDesc);
extern int SPI_exec(const char *src, int tcount);
extern int SPI_execp(void *plan, Datum *values, const char *Nulls,
int tcount);
+extern int SPI_execp_now(void *plan, Datum *values, const char *Nulls,
+ int tcount);
extern void *SPI_prepare(const char *src, int nargs, Oid *argtypes);
extern void *SPI_saveplan(void *plan);
extern int SPI_freeplan(void *plan);
/* Basic state for all query types: */
ScanDirection es_direction; /* current scan direction */
Snapshot es_snapshot; /* time qual to use */
+ CommandId es_snapshot_cid; /* CommandId component of time qual */
List *es_range_table; /* List of RangeTableEntrys */
/* Info about target table for insert/update/delete queries: */
extern TransactionId RecentXmin;
extern TransactionId RecentGlobalXmin;
-extern bool ReferentialIntegritySnapshotOverride;
-
-#define IsSnapshotNow(snapshot) ((Snapshot) (snapshot) == SnapshotNow)
-#define IsSnapshotSelf(snapshot) ((Snapshot) (snapshot) == SnapshotSelf)
-#define IsSnapshotAny(snapshot) ((Snapshot) (snapshot) == SnapshotAny)
-#define IsSnapshotToast(snapshot) ((Snapshot) (snapshot) == SnapshotToast)
-#define IsSnapshotDirty(snapshot) ((Snapshot) (snapshot) == SnapshotDirty)
-
/*
* HeapTupleSatisfiesVisibility
* Beware of multiple evaluations of snapshot argument.
*/
#define HeapTupleSatisfiesVisibility(tuple, snapshot) \
-(IsSnapshotNow(snapshot) ? \
+((snapshot) == SnapshotNow ? \
HeapTupleSatisfiesNow((tuple)->t_data) \
: \
- (IsSnapshotSelf(snapshot) ? \
+ ((snapshot) == SnapshotSelf ? \
HeapTupleSatisfiesItself((tuple)->t_data) \
: \
- (IsSnapshotAny(snapshot) ? \
+ ((snapshot) == SnapshotAny ? \
true \
: \
- (IsSnapshotToast(snapshot) ? \
+ ((snapshot) == SnapshotToast ? \
HeapTupleSatisfiesToast((tuple)->t_data) \
: \
- (IsSnapshotDirty(snapshot) ? \
+ ((snapshot) == SnapshotDirty ? \
HeapTupleSatisfiesDirty((tuple)->t_data) \
: \
HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \