Commit 25b9b1b0 authored by Tom Lane's avatar Tom Lane

Repair "Halloween problem" in EvalPlanQual: a tuple that's been inserted by

our own command (or more generally, xmin = our xact and cmin >= current
command ID) should not be seen as good.  Else we may try to update rows
we already updated.  This error was inserted last August while fixing the
even bigger problem that the old coding wouldn't see *any* tuples inserted
by our own transaction as good.  Per report from Euler Taveira de Oliveira.
parent db0558c1
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.198 2006/01/05 10:07:45 petere Exp $ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.199 2006/01/12 21:48:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1736,7 +1736,8 @@ ltrmark:; ...@@ -1736,7 +1736,8 @@ ltrmark:;
epqslot = EvalPlanQual(estate, epqslot = EvalPlanQual(estate,
relinfo->ri_RangeTableIndex, relinfo->ri_RangeTableIndex,
&update_ctid, &update_ctid,
update_xmax); update_xmax,
cid);
if (!TupIsNull(epqslot)) if (!TupIsNull(epqslot))
{ {
*tid = update_ctid; *tid = update_ctid;
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.264 2006/01/11 08:43:12 neilc Exp $ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.265 2006/01/12 21:48:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1213,7 +1213,8 @@ lnext: ; ...@@ -1213,7 +1213,8 @@ lnext: ;
newSlot = EvalPlanQual(estate, newSlot = EvalPlanQual(estate,
erm->rti, erm->rti,
&update_ctid, &update_ctid,
update_xmax); update_xmax,
estate->es_snapshot->curcid);
if (!TupIsNull(newSlot)) if (!TupIsNull(newSlot))
{ {
slot = newSlot; slot = newSlot;
...@@ -1521,7 +1522,8 @@ ldelete:; ...@@ -1521,7 +1522,8 @@ ldelete:;
epqslot = EvalPlanQual(estate, epqslot = EvalPlanQual(estate,
resultRelInfo->ri_RangeTableIndex, resultRelInfo->ri_RangeTableIndex,
&update_ctid, &update_ctid,
update_xmax); update_xmax,
estate->es_snapshot->curcid);
if (!TupIsNull(epqslot)) if (!TupIsNull(epqslot))
{ {
*tupleid = update_ctid; *tupleid = update_ctid;
...@@ -1673,7 +1675,8 @@ lreplace:; ...@@ -1673,7 +1675,8 @@ lreplace:;
epqslot = EvalPlanQual(estate, epqslot = EvalPlanQual(estate,
resultRelInfo->ri_RangeTableIndex, resultRelInfo->ri_RangeTableIndex,
&update_ctid, &update_ctid,
update_xmax); update_xmax,
estate->es_snapshot->curcid);
if (!TupIsNull(epqslot)) if (!TupIsNull(epqslot))
{ {
*tupleid = update_ctid; *tupleid = update_ctid;
...@@ -1820,6 +1823,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -1820,6 +1823,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
* rti - rangetable index of table containing tuple * rti - rangetable index of table containing tuple
* *tid - t_ctid from the outdated tuple (ie, next updated version) * *tid - t_ctid from the outdated tuple (ie, next updated version)
* priorXmax - t_xmax from the outdated tuple * priorXmax - t_xmax from the outdated tuple
* curCid - command ID of current command of my transaction
* *
* *tid is also an output parameter: it's modified to hold the TID of the * *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) * latest version of the tuple (note this may be changed even on failure)
...@@ -1829,7 +1833,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -1829,7 +1833,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
*/ */
TupleTableSlot * TupleTableSlot *
EvalPlanQual(EState *estate, Index rti, EvalPlanQual(EState *estate, Index rti,
ItemPointer tid, TransactionId priorXmax) ItemPointer tid, TransactionId priorXmax, CommandId curCid)
{ {
evalPlanQual *epq; evalPlanQual *epq;
EState *epqstate; EState *epqstate;
...@@ -1905,6 +1909,24 @@ EvalPlanQual(EState *estate, Index rti, ...@@ -1905,6 +1909,24 @@ EvalPlanQual(EState *estate, Index rti,
continue; /* loop back to repeat heap_fetch */ continue; /* loop back to repeat heap_fetch */
} }
/*
* If tuple was inserted by our own transaction, we have to check
* cmin against curCid: cmin >= curCid means our command cannot
* see the tuple, so we should ignore it. Without this we are
* open to the "Halloween problem" of indefinitely re-updating
* the same tuple. (We need not check cmax because
* HeapTupleSatisfiesDirty will consider a tuple deleted by
* our transaction dead, regardless of cmax.) We just checked
* that priorXmax == xmin, so we can test that variable instead
* of doing HeapTupleHeaderGetXmin again.
*/
if (TransactionIdIsCurrentTransactionId(priorXmax) &&
HeapTupleHeaderGetCmin(tuple.t_data) >= curCid)
{
ReleaseBuffer(buffer);
return NULL;
}
/* /*
* We got tuple - now copy it for use by recheck query. * We got tuple - now copy it for use by recheck query.
*/ */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.123 2005/12/03 05:51:03 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.124 2006/01/12 21:48:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -98,7 +98,7 @@ extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); ...@@ -98,7 +98,7 @@ extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
extern void ExecConstraints(ResultRelInfo *resultRelInfo, extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate); TupleTableSlot *slot, EState *estate);
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
ItemPointer tid, TransactionId priorXmax); ItemPointer tid, TransactionId priorXmax, CommandId curCid);
/* /*
* prototypes from functions in execProcnode.c * prototypes from functions in execProcnode.c
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment