Repair "Halloween problem" in EvalPlanQual: a tuple that's been inserted by
authorTom Lane <[email protected]>
Thu, 12 Jan 2006 21:49:19 +0000 (21:49 +0000)
committerTom Lane <[email protected]>
Thu, 12 Jan 2006 21:49:19 +0000 (21:49 +0000)
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.

src/backend/commands/trigger.c
src/backend/executor/execMain.c
src/include/executor/executor.h

index ee2f5b85c15a379b0db30b1ddb25775abeef3614..232e6a83354455a79a773059c1ee22349692fad8 100644 (file)
@@ -1603,7 +1603,8 @@ ltrmark:;
                                        epqslot = EvalPlanQual(estate,
                                                                                   relinfo->ri_RangeTableIndex,
                                                                                   &update_ctid,
-                                                                                  update_xmax);
+                                                                                  update_xmax,
+                                                                                  cid);
                                        if (!TupIsNull(epqslot))
                                        {
                                                *tid = update_ctid;
index be00dffbc40edb77f6a29529fd89b440f87d276f..ebf05c82cb3eb947a648b773c63d88c59ba5fd29 100644 (file)
@@ -1159,7 +1159,8 @@ lnext:    ;
                                                                newSlot = EvalPlanQual(estate,
                                                                                                           erm->rti,
                                                                                                           &update_ctid,
-                                                                                                          update_xmax);
+                                                                                                          update_xmax,
+                                                                                                          estate->es_snapshot->curcid);
                                                                if (!TupIsNull(newSlot))
                                                                {
                                                                        slot = newSlot;
@@ -1470,7 +1471,8 @@ ldelete:;
                                epqslot = EvalPlanQual(estate,
                                                                           resultRelInfo->ri_RangeTableIndex,
                                                                           &update_ctid,
-                                                                          update_xmax);
+                                                                          update_xmax,
+                                                                          estate->es_snapshot->curcid);
                                if (!TupIsNull(epqslot))
                                {
                                        *tupleid = update_ctid;
@@ -1615,7 +1617,8 @@ lreplace:;
                                epqslot = EvalPlanQual(estate,
                                                                           resultRelInfo->ri_RangeTableIndex,
                                                                           &update_ctid,
-                                                                          update_xmax);
+                                                                          update_xmax,
+                                                                          estate->es_snapshot->curcid);
                                if (!TupIsNull(epqslot))
                                {
                                        *tupleid = update_ctid;
@@ -1770,6 +1773,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
  *     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)
@@ -1779,7 +1783,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
  */
 TupleTableSlot *
 EvalPlanQual(EState *estate, Index rti,
-                        ItemPointer tid, TransactionId priorXmax)
+                        ItemPointer tid, TransactionId priorXmax, CommandId curCid)
 {
        evalPlanQual *epq;
        EState     *epqstate;
@@ -1855,6 +1859,24 @@ EvalPlanQual(EState *estate, Index rti,
                                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.
                         */
index 385fc600f370311e791d89df9b1c573d85579f5b..149587dc08d423d483f7932d936c91bd62039772 100644 (file)
@@ -109,7 +109,7 @@ extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
                                TupleTableSlot *slot, EState *estate);
 extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
-                                                                       ItemPointer tid, TransactionId priorXmax);
+                               ItemPointer tid, TransactionId priorXmax, CommandId curCid);
 
 /*
  * prototypes from functions in execProcnode.c