Re: BUG #2166: attempted to update invisible tuple

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: Euler Taveira de Oliveira <eulerto(at)yahoo(dot)com(dot)br>
Cc: pgsql-bugs(at)postgresql(dot)org
Subject: Re: BUG #2166: attempted to update invisible tuple
Date: 2006-01-12 21:51:30
Message-ID: 18756.1137102690@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

I wrote:
> It looks to me like the correct test requires passing in the current
> command ID so we can check the tuple's cmax against it.

Er, cmin not cmax. Here's the patch against 8.1 if you need it.

regards, tom lane

Index: src/backend/commands/trigger.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/trigger.c,v
retrieving revision 1.195.2.1
diff -c -r1.195.2.1 trigger.c
*** src/backend/commands/trigger.c 22 Nov 2005 18:23:07 -0000 1.195.2.1
--- src/backend/commands/trigger.c 12 Jan 2006 21:10:09 -0000
***************
*** 1736,1742 ****
epqslot = EvalPlanQual(estate,
relinfo->ri_RangeTableIndex,
&update_ctid,
! update_xmax);
if (!TupIsNull(epqslot))
{
*tid = update_ctid;
--- 1736,1743 ----
epqslot = EvalPlanQual(estate,
relinfo->ri_RangeTableIndex,
&update_ctid,
! update_xmax,
! cid);
if (!TupIsNull(epqslot))
{
*tid = update_ctid;
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.256.2.4
diff -c -r1.256.2.4 execMain.c
*** src/backend/executor/execMain.c 22 Nov 2005 18:23:08 -0000 1.256.2.4
--- src/backend/executor/execMain.c 12 Jan 2006 21:10:09 -0000
***************
*** 1219,1225 ****
newSlot = EvalPlanQual(estate,
erm->rti,
&update_ctid,
! update_xmax);
if (!TupIsNull(newSlot))
{
slot = newSlot;
--- 1219,1226 ----
newSlot = EvalPlanQual(estate,
erm->rti,
&update_ctid,
! update_xmax,
! estate->es_snapshot->curcid);
if (!TupIsNull(newSlot))
{
slot = newSlot;
***************
*** 1527,1533 ****
epqslot = EvalPlanQual(estate,
resultRelInfo->ri_RangeTableIndex,
&update_ctid,
! update_xmax);
if (!TupIsNull(epqslot))
{
*tupleid = update_ctid;
--- 1528,1535 ----
epqslot = EvalPlanQual(estate,
resultRelInfo->ri_RangeTableIndex,
&update_ctid,
! update_xmax,
! estate->es_snapshot->curcid);
if (!TupIsNull(epqslot))
{
*tupleid = update_ctid;
***************
*** 1679,1685 ****
epqslot = EvalPlanQual(estate,
resultRelInfo->ri_RangeTableIndex,
&update_ctid,
! update_xmax);
if (!TupIsNull(epqslot))
{
*tupleid = update_ctid;
--- 1681,1688 ----
epqslot = EvalPlanQual(estate,
resultRelInfo->ri_RangeTableIndex,
&update_ctid,
! update_xmax,
! estate->es_snapshot->curcid);
if (!TupIsNull(epqslot))
{
*tupleid = update_ctid;
***************
*** 1826,1831 ****
--- 1829,1835 ----
* 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
+ * curCid - command ID of current command of my transaction
*
* *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)
***************
*** 1835,1841 ****
*/
TupleTableSlot *
EvalPlanQual(EState *estate, Index rti,
! ItemPointer tid, TransactionId priorXmax)
{
evalPlanQual *epq;
EState *epqstate;
--- 1839,1845 ----
*/
TupleTableSlot *
EvalPlanQual(EState *estate, Index rti,
! ItemPointer tid, TransactionId priorXmax, CommandId curCid)
{
evalPlanQual *epq;
EState *epqstate;
***************
*** 1912,1917 ****
--- 1916,1939 ----
}

/*
+ * 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.
*/
copyTuple = heap_copytuple(&tuple);
Index: src/include/executor/executor.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/executor/executor.h,v
retrieving revision 1.120.2.1
diff -c -r1.120.2.1 executor.h
*** src/include/executor/executor.h 23 Nov 2005 20:28:05 -0000 1.120.2.1
--- src/include/executor/executor.h 12 Jan 2006 21:10:10 -0000
***************
*** 98,104 ****
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate);
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
! ItemPointer tid, TransactionId priorXmax);

/*
* prototypes from functions in execProcnode.c
--- 98,104 ----
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate);
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
! ItemPointer tid, TransactionId priorXmax, CommandId curCid);

/*
* prototypes from functions in execProcnode.c

In response to

Browse pgsql-bugs by date

  From Date Subject
Next Message Sunil Basu 2006-01-13 04:39:34 BUG #2167: Performance degradation
Previous Message Tom Lane 2006-01-12 19:54:24 Re: BUG #2166: attempted to update invisible tuple