summaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execMain.c137
1 files changed, 103 insertions, 34 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 622cfa93713..b6684579f1b 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.180.2.2 2003/03/27 14:33:21 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.180.2.3 2005/08/26 20:07:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1012,8 +1012,10 @@ lnext: ;
foreach(l, estate->es_rowMark)
{
execRowMark *erm = lfirst(l);
- Buffer buffer;
HeapTupleData tuple;
+ Buffer buffer;
+ ItemPointerData update_ctid;
+ TransactionId update_xmax;
TupleTableSlot *newSlot;
int test;
@@ -1032,6 +1034,7 @@ lnext: ;
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
test = heap_mark4update(erm->relation, &tuple, &buffer,
+ &update_ctid, &update_xmax,
estate->es_snapshot->curcid);
ReleaseBuffer(buffer);
switch (test)
@@ -1046,11 +1049,15 @@ lnext: ;
case HeapTupleUpdated:
if (XactIsoLevel == XACT_SERIALIZABLE)
elog(ERROR, "Can't serialize access due to concurrent update");
- if (!(ItemPointerEquals(&(tuple.t_self),
- (ItemPointer) DatumGetPointer(datum))))
+ if (!ItemPointerEquals(&update_ctid,
+ &tuple.t_self))
{
- newSlot = EvalPlanQual(estate, erm->rti, &(tuple.t_self));
- if (!(TupIsNull(newSlot)))
+ /* updated, so look at updated version */
+ newSlot = EvalPlanQual(estate,
+ erm->rti,
+ &update_ctid,
+ update_xmax);
+ if (!TupIsNull(newSlot))
{
slot = newSlot;
estate->es_useEvalPlan = true;
@@ -1280,8 +1287,9 @@ ExecDelete(TupleTableSlot *slot,
{
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
- ItemPointerData ctid;
int result;
+ ItemPointerData update_ctid;
+ TransactionId update_xmax;
/*
* get information on the (current) result relation
@@ -1307,7 +1315,7 @@ ExecDelete(TupleTableSlot *slot,
*/
ldelete:;
result = heap_delete(resultRelationDesc, tupleid,
- &ctid,
+ &update_ctid, &update_xmax,
estate->es_snapshot->curcid);
switch (result)
{
@@ -1321,14 +1329,17 @@ ldelete:;
case HeapTupleUpdated:
if (XactIsoLevel == XACT_SERIALIZABLE)
elog(ERROR, "Can't serialize access due to concurrent update");
- else if (!(ItemPointerEquals(tupleid, &ctid)))
+ else if (!ItemPointerEquals(tupleid, &update_ctid))
{
- TupleTableSlot *epqslot = EvalPlanQual(estate,
- resultRelInfo->ri_RangeTableIndex, &ctid);
+ TupleTableSlot *epqslot;
+ epqslot = EvalPlanQual(estate,
+ resultRelInfo->ri_RangeTableIndex,
+ &update_ctid,
+ update_xmax);
if (!TupIsNull(epqslot))
{
- *tupleid = ctid;
+ *tupleid = update_ctid;
goto ldelete;
}
}
@@ -1376,8 +1387,9 @@ ExecUpdate(TupleTableSlot *slot,
HeapTuple tuple;
ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
- ItemPointerData ctid;
int result;
+ ItemPointerData update_ctid;
+ TransactionId update_xmax;
int numIndices;
/*
@@ -1443,7 +1455,7 @@ lreplace:;
* replace the heap tuple
*/
result = heap_update(resultRelationDesc, tupleid, tuple,
- &ctid,
+ &update_ctid, &update_xmax,
estate->es_snapshot->curcid);
switch (result)
{
@@ -1457,14 +1469,17 @@ lreplace:;
case HeapTupleUpdated:
if (XactIsoLevel == XACT_SERIALIZABLE)
elog(ERROR, "Can't serialize access due to concurrent update");
- else if (!(ItemPointerEquals(tupleid, &ctid)))
+ else if (!(ItemPointerEquals(tupleid, &update_ctid)))
{
- TupleTableSlot *epqslot = EvalPlanQual(estate,
- resultRelInfo->ri_RangeTableIndex, &ctid);
+ TupleTableSlot *epqslot;
+ epqslot = EvalPlanQual(estate,
+ resultRelInfo->ri_RangeTableIndex,
+ &update_ctid,
+ update_xmax);
if (!TupIsNull(epqslot))
{
- *tupleid = ctid;
+ *tupleid = update_ctid;
tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot);
slot = ExecStoreTuple(tuple,
estate->es_junkFilter->jf_resultSlot,
@@ -1606,9 +1621,21 @@ ExecConstraints(const char *caller, ResultRelInfo *resultRelInfo,
* under READ COMMITTED rules.
*
* See backend/executor/README for some info about how this works.
+ *
+ * estate - executor state data
+ * rti - rangetable index of table containing tuple
+ * *tid - t_ctid from the outdated tuple (ie, next updated version)
+ * priorXmax - t_xmax from the outdated tuple
+ *
+ * *tid is also an output parameter: it's modified to hold the TID of the
+ * latest version of the tuple (note this may be changed even on failure)
+ *
+ * Returns a slot containing the new candidate update/delete tuple, or
+ * NULL if we determine we shouldn't process the row.
*/
TupleTableSlot *
-EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
+EvalPlanQual(EState *estate, Index rti,
+ ItemPointer tid, TransactionId priorXmax)
{
evalPlanQual *epq;
EState *epqstate;
@@ -1653,10 +1680,24 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
{
Buffer buffer;
- if (heap_fetch(relation, SnapshotDirty, &tuple, &buffer, false, NULL))
+ if (heap_fetch(relation, SnapshotDirty, &tuple, &buffer, true, NULL))
{
- TransactionId xwait = SnapshotDirty->xmax;
+ /*
+ * If xmin isn't what we're expecting, the slot must have been
+ * recycled and reused for an unrelated tuple. This implies
+ * that the latest version of the row was deleted, so we need
+ * do nothing. (Should be safe to examine xmin without getting
+ * buffer's content lock, since xmin never changes in an existing
+ * tuple.)
+ */
+ if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+ priorXmax))
+ {
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
+ /* otherwise xmin should not be dirty... */
if (TransactionIdIsValid(SnapshotDirty->xmin))
elog(ERROR, "EvalPlanQual: t_xmin is uncommitted ?!");
@@ -1664,11 +1705,11 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
* If tuple is being updated by other transaction then we have
* to wait for its commit/abort.
*/
- if (TransactionIdIsValid(xwait))
+ if (TransactionIdIsValid(SnapshotDirty->xmax))
{
ReleaseBuffer(buffer);
- XactLockTableWait(xwait);
- continue;
+ XactLockTableWait(SnapshotDirty->xmax);
+ continue; /* loop back to repeat heap_fetch */
}
/*
@@ -1680,22 +1721,50 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
}
/*
- * Oops! Invalid tuple. Have to check is it updated or deleted.
- * Note that it's possible to get invalid SnapshotDirty->tid if
- * tuple updated by this transaction. Have we to check this ?
+ * If the referenced slot was actually empty, the latest version
+ * of the row must have been deleted, so we need do nothing.
+ */
+ if (tuple.t_data == NULL)
+ {
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
+
+ /*
+ * As above, if xmin isn't what we're expecting, do nothing.
*/
- if (ItemPointerIsValid(&(SnapshotDirty->tid)) &&
- !(ItemPointerEquals(&(tuple.t_self), &(SnapshotDirty->tid))))
+ if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data),
+ priorXmax))
{
- /* updated, so look at the updated copy */
- tuple.t_self = SnapshotDirty->tid;
- continue;
+ ReleaseBuffer(buffer);
+ return NULL;
}
/*
- * Deleted or updated by this transaction; forget it.
+ * If we get here, the tuple was found but failed SnapshotDirty.
+ * Assuming the xmin is either a committed xact or our own xact
+ * (as it certainly should be if we're trying to modify the tuple),
+ * this must mean that the row was updated or deleted by either
+ * a committed xact or our own xact. If it was deleted, we can
+ * ignore it; if it was updated then chain up to the next version
+ * and repeat the whole test.
+ *
+ * As above, it should be safe to examine xmax and t_ctid without
+ * the buffer content lock, because they can't be changing.
*/
- return NULL;
+ if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid))
+ {
+ /* deleted, so forget about it */
+ ReleaseBuffer(buffer);
+ return NULL;
+ }
+
+ /* updated, so look at the updated row */
+ tuple.t_self = tuple.t_data->t_ctid;
+ /* updated row should have xmin matching this xmax */
+ priorXmax = HeapTupleHeaderGetXmax(tuple.t_data);
+ ReleaseBuffer(buffer);
+ /* loop back to fetch next in chain */
}
/*