Avoid incrementing the CommandCounter when CommandCounterIncrement is called
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 30 Nov 2007 21:22:54 +0000 (21:22 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 30 Nov 2007 21:22:54 +0000 (21:22 +0000)
but no database changes have been made since the last CommandCounterIncrement.
This should result in a significant improvement in the number of "commands"
that can typically be performed within a transaction before hitting the 2^32
CommandId size limit.  In particular this buys back (and more) the possible
adverse consequences of my previous patch to fix plan caching behavior.

The implementation requires tracking whether the current CommandCounter
value has been "used" to mark any tuples.  CommandCounter values stored into
snapshots are presumed not to be used for this purpose.  This requires some
small executor changes, since the executor used to conflate the curcid of
the snapshot it was using with the command ID to mark output tuples with.
Separating these concepts allows some small simplifications in executor APIs.

Something for the TODO list: look into having CommandCounterIncrement not do
AcceptInvalidationMessages.  It seems fairly bogus to be doing it there,
but exactly where to do it instead isn't clear, and I'm disinclined to mess
with asynchronous behavior during late beta.

20 files changed:
contrib/pgrowlocks/pgrowlocks.c
src/backend/access/heap/heapam.c
src/backend/access/heap/tuptoaster.c
src/backend/access/transam/xact.c
src/backend/commands/async.c
src/backend/commands/copy.c
src/backend/commands/explain.c
src/backend/commands/trigger.c
src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/backend/executor/spi.c
src/backend/storage/ipc/procarray.c
src/backend/utils/cache/inval.c
src/backend/utils/time/tqual.c
src/include/access/xact.h
src/include/commands/trigger.h
src/include/executor/executor.h
src/include/nodes/execnodes.h
src/test/regress/expected/combocid.out
src/test/regress/sql/combocid.sql

index 2e8c668a6c4de991d91c2441111dad390ab32d5f..c191e95466ae8a519da8076a0060145f2973415e 100644 (file)
@@ -117,8 +117,9 @@ pgrowlocks(PG_FUNCTION_ARGS)
                /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */
                LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
 
-               if (HeapTupleSatisfiesUpdate(tuple->t_data, GetCurrentCommandId(), scan->rs_cbuf)
-                       == HeapTupleBeingUpdated)
+               if (HeapTupleSatisfiesUpdate(tuple->t_data,
+                                                                        GetCurrentCommandId(false),
+                                                                        scan->rs_cbuf) == HeapTupleBeingUpdated)
                {
 
                        char      **values;
index 9e4fbfbbba1d5afc47b6cb7c38aef9bb19d080f1..026102ec7f59498f510547d04f1faa8761bab529 100644 (file)
@@ -1891,7 +1891,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
 Oid
 simple_heap_insert(Relation relation, HeapTuple tup)
 {
-       return heap_insert(relation, tup, GetCurrentCommandId(), true, true);
+       return heap_insert(relation, tup, GetCurrentCommandId(true), true, true);
 }
 
 /*
@@ -2174,7 +2174,7 @@ simple_heap_delete(Relation relation, ItemPointer tid)
 
        result = heap_delete(relation, tid,
                                                 &update_ctid, &update_xmax,
-                                                GetCurrentCommandId(), InvalidSnapshot,
+                                                GetCurrentCommandId(true), InvalidSnapshot,
                                                 true /* wait for commit */ );
        switch (result)
        {
@@ -2817,7 +2817,7 @@ simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
 
        result = heap_update(relation, otid, tup,
                                                 &update_ctid, &update_xmax,
-                                                GetCurrentCommandId(), InvalidSnapshot,
+                                                GetCurrentCommandId(true), InvalidSnapshot,
                                                 true /* wait for commit */ );
        switch (result)
        {
index 2f3d7c2c36bc879525f0a1044d4d199c454d30ca..0c8c55d07d86032e2e4f4eee8e30b363c05eacd6 100644 (file)
@@ -1086,7 +1086,7 @@ toast_save_datum(Relation rel, Datum value,
        TupleDesc       toasttupDesc;
        Datum           t_values[3];
        bool            t_isnull[3];
-       CommandId       mycid = GetCurrentCommandId();
+       CommandId       mycid = GetCurrentCommandId(true);
        struct varlena *result;
        struct varatt_external toast_pointer;
        struct
index ae5a0010af7073fb1878a5bb0682b362d09749b9..f9af223ec0820b123fb68122d7b21854b66cd2f8 100644 (file)
@@ -161,6 +161,7 @@ static TransactionState CurrentTransactionState = &TopTransactionStateData;
  */
 static SubTransactionId currentSubTransactionId;
 static CommandId currentCommandId;
+static bool currentCommandIdUsed;
 
 /*
  * xactStartTimestamp is the value of transaction_timestamp().
@@ -435,11 +436,18 @@ GetCurrentSubTransactionId(void)
 
 /*
  *     GetCurrentCommandId
+ *
+ * "used" must be TRUE if the caller intends to use the command ID to mark
+ * inserted/updated/deleted tuples.  FALSE means the ID is being fetched
+ * for read-only purposes (ie, as a snapshot validity cutoff).  See
+ * CommandCounterIncrement() for discussion.
  */
 CommandId
-GetCurrentCommandId(void)
+GetCurrentCommandId(bool used)
 {
        /* this is global to a transaction, not subtransaction-local */
+       if (used)
+               currentCommandIdUsed = true;
        return currentCommandId;
 }
 
@@ -566,25 +574,50 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 void
 CommandCounterIncrement(void)
 {
-       currentCommandId += 1;
-       if (currentCommandId == FirstCommandId)         /* check for overflow */
+       /*
+        * If the current value of the command counter hasn't been "used" to
+        * mark tuples, we need not increment it, since there's no need to
+        * distinguish a read-only command from others.  This helps postpone
+        * command counter overflow, and keeps no-op CommandCounterIncrement
+        * operations cheap.
+        */
+       if (currentCommandIdUsed)
        {
-               currentCommandId -= 1;
-               ereport(ERROR,
-                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+               currentCommandId += 1;
+               if (currentCommandId == FirstCommandId) /* check for overflow */
+               {
+                       currentCommandId -= 1;
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                  errmsg("cannot have more than 2^32-1 commands in a transaction")));
-       }
+               }
+               currentCommandIdUsed = false;
 
-       /* Propagate new command ID into static snapshots, if set */
-       if (SerializableSnapshot)
-               SerializableSnapshot->curcid = currentCommandId;
-       if (LatestSnapshot)
-               LatestSnapshot->curcid = currentCommandId;
+               /* Propagate new command ID into static snapshots, if set */
+               if (SerializableSnapshot)
+                       SerializableSnapshot->curcid = currentCommandId;
+               if (LatestSnapshot)
+                       LatestSnapshot->curcid = currentCommandId;
+
+               /*
+                * Make any catalog changes done by the just-completed command
+                * visible in the local syscache.  We obviously don't need to do
+                * this after a read-only command.  (But see hacks in inval.c
+                * to make real sure we don't think a command that queued inval
+                * messages was read-only.)
+                */
+               AtCommit_LocalCache();
+       }
 
        /*
-        * make cache changes visible to me.
+        * Make any other backends' catalog changes visible to me.
+        *
+        * XXX this is probably in the wrong place: CommandCounterIncrement
+        * should be purely a local operation, most likely.  However fooling
+        * with this will affect asynchronous cross-backend interactions,
+        * which doesn't seem like a wise thing to do in late beta, so save
+        * improving this for another day - tgl 2007-11-30
         */
-       AtCommit_LocalCache();
        AtStart_Cache();
 }
 
@@ -1416,6 +1449,7 @@ StartTransaction(void)
        s->subTransactionId = TopSubTransactionId;
        currentSubTransactionId = TopSubTransactionId;
        currentCommandId = FirstCommandId;
+       currentCommandIdUsed = false;
 
        /*
         * must initialize resource-management stuff first
@@ -4007,13 +4041,14 @@ ShowTransactionStateRec(TransactionState s)
 
        /* use ereport to suppress computation if msg will not be printed */
        ereport(DEBUG3,
-                       (errmsg_internal("name: %s; blockState: %13s; state: %7s, xid/subid/cid: %u/%u/%u, nestlvl: %d, children: %s",
+                       (errmsg_internal("name: %s; blockState: %13s; state: %7s, xid/subid/cid: %u/%u/%u%s, nestlvl: %d, children: %s",
                                                         PointerIsValid(s->name) ? s->name : "unnamed",
                                                         BlockStateAsString(s->blockState),
                                                         TransStateAsString(s->state),
                                                         (unsigned int) s->transactionId,
                                                         (unsigned int) s->subTransactionId,
                                                         (unsigned int) currentCommandId,
+                                                        currentCommandIdUsed ? " (used)" : "",
                                                         s->nestingLevel,
                                                         nodeToString(s->childXids))));
 }
index 9a53e51f6598134f4be5b97ab95e34a7c8909bdd..45ea0703b367365e7bb4a47f3818cc6b0d7f86b8 100644 (file)
@@ -562,7 +562,8 @@ AtCommit_Notify(void)
                                 */
                                result = heap_update(lRel, &lTuple->t_self, rTuple,
                                                                         &update_ctid, &update_xmax,
-                                                                        GetCurrentCommandId(), InvalidSnapshot,
+                                                                        GetCurrentCommandId(true),
+                                                                        InvalidSnapshot,
                                                                         false /* no wait for commit */ );
                                switch (result)
                                {
index 1eb06a3e4eefbfc18c6220322f63e07cb1658efd..87061bc57740097d56c5bb75efc13d9dc00f2462 100644 (file)
@@ -1033,7 +1033,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
                 * which COPY can be invoked, I think it's OK, because the active
                 * snapshot shouldn't be shared with anything else anyway.)
                 */
-               ActiveSnapshot->curcid = GetCurrentCommandId();
+               ActiveSnapshot->curcid = GetCurrentCommandId(false);
 
                /* Create dest receiver for COPY OUT */
                dest = CreateDestReceiver(DestCopyOut, NULL);
@@ -1637,7 +1637,7 @@ CopyFrom(CopyState cstate)
        ExprContext *econtext;          /* used for ExecEvalExpr for default atts */
        MemoryContext oldcontext = CurrentMemoryContext;
        ErrorContextCallback errcontext;
-       CommandId       mycid = GetCurrentCommandId();
+       CommandId       mycid = GetCurrentCommandId(true);
        bool            use_wal = true; /* by default, use WAL logging */
        bool            use_fsm = true; /* by default, use FSM for free space */
 
index 372c0cd89ad69c1028b2b82c3850dc60bbc33cc8..2e91ce252af81903268b4d37c09655ac137356db 100644 (file)
@@ -233,7 +233,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params,
         * EXPLAIN can be invoked, I think it's OK, because the active snapshot
         * shouldn't be shared with anything else anyway.)
         */
-       ActiveSnapshot->curcid = GetCurrentCommandId();
+       ActiveSnapshot->curcid = GetCurrentCommandId(false);
 
        /* Create a QueryDesc requesting no output */
        queryDesc = CreateQueryDesc(plannedstmt,
index 373a976cace03c59fc549b79602a0691b2f55ae3..3bbf2e72a6e2961d0694587358d2ca874ac9e35a 100644 (file)
@@ -51,7 +51,6 @@ static void InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx);
 static HeapTuple GetTupleForTrigger(EState *estate,
                                   ResultRelInfo *relinfo,
                                   ItemPointer tid,
-                                  CommandId cid,
                                   TupleTableSlot **newSlot);
 static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
                                        int tgindx,
@@ -1801,8 +1800,7 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
 
 bool
 ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
-                                        ItemPointer tupleid,
-                                        CommandId cid)
+                                        ItemPointer tupleid)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
        int                     ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_DELETE];
@@ -1814,7 +1812,7 @@ ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
        TupleTableSlot *newSlot;
        int                     i;
 
-       trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, cid, &newSlot);
+       trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
        if (trigtuple == NULL)
                return false;
 
@@ -1871,9 +1869,7 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
        if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
        {
                HeapTuple       trigtuple = GetTupleForTrigger(estate, relinfo,
-                                                                                                  tupleid,
-                                                                                                  (CommandId) 0,
-                                                                                                  NULL);
+                                                                                                  tupleid, NULL);
 
                AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
                                                          true, trigtuple, NULL);
@@ -1952,8 +1948,7 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
 
 HeapTuple
 ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
-                                        ItemPointer tupleid, HeapTuple newtuple,
-                                        CommandId cid)
+                                        ItemPointer tupleid, HeapTuple newtuple)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
        int                     ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_UPDATE];
@@ -1965,7 +1960,7 @@ ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
        TupleTableSlot *newSlot;
        int                     i;
 
-       trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, cid, &newSlot);
+       trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
        if (trigtuple == NULL)
                return NULL;
 
@@ -2025,9 +2020,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
        if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0)
        {
                HeapTuple       trigtuple = GetTupleForTrigger(estate, relinfo,
-                                                                                                  tupleid,
-                                                                                                  (CommandId) 0,
-                                                                                                  NULL);
+                                                                                                  tupleid, NULL);
 
                AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
                                                          true, trigtuple, newtuple);
@@ -2038,7 +2031,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 
 static HeapTuple
 GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
-                                  ItemPointer tid, CommandId cid,
+                                  ItemPointer tid,
                                   TupleTableSlot **newSlot)
 {
        Relation        relation = relinfo->ri_RelationDesc;
@@ -2060,7 +2053,8 @@ GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
 ltrmark:;
                tuple.t_self = *tid;
                test = heap_lock_tuple(relation, &tuple, &buffer,
-                                                          &update_ctid, &update_xmax, cid,
+                                                          &update_ctid, &update_xmax,
+                                                          estate->es_output_cid,
                                                           LockTupleExclusive, false);
                switch (test)
                {
@@ -2086,8 +2080,7 @@ ltrmark:;
                                        epqslot = EvalPlanQual(estate,
                                                                                   relinfo->ri_RangeTableIndex,
                                                                                   &update_ctid,
-                                                                                  update_xmax,
-                                                                                  cid);
+                                                                                  update_xmax);
                                        if (!TupIsNull(epqslot))
                                        {
                                                *tid = update_ctid;
index 97729a5e5d26566a9141bf9e207ea584d1604e4e..7a72daf705d84e5f9db78f41993550de265fa33c 100644 (file)
@@ -160,6 +160,30 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
                estate->es_param_exec_vals = (ParamExecData *)
                        palloc0(queryDesc->plannedstmt->nParamExec * sizeof(ParamExecData));
 
+       /*
+        * If non-read-only query, set the command ID to mark output tuples with
+        */
+       switch (queryDesc->operation)
+       {
+               case CMD_SELECT:
+                       /* SELECT INTO and SELECT FOR UPDATE/SHARE need to mark tuples */
+                       if (queryDesc->plannedstmt->intoClause != NULL ||
+                               queryDesc->plannedstmt->rowMarks != NIL)
+                               estate->es_output_cid = GetCurrentCommandId(true);
+                       break;
+
+               case CMD_INSERT:
+               case CMD_DELETE:
+               case CMD_UPDATE:
+                       estate->es_output_cid = GetCurrentCommandId(true);
+                       break;
+
+               default:
+                       elog(ERROR, "unrecognized operation code: %d",
+                                (int) queryDesc->operation);
+                       break;
+       }
+
        /*
         * Copy other important information into the EState
         */
@@ -1285,7 +1309,7 @@ lnext:    ;
 
                                        test = heap_lock_tuple(erm->relation, &tuple, &buffer,
                                                                                   &update_ctid, &update_xmax,
-                                                                                  estate->es_snapshot->curcid,
+                                                                                  estate->es_output_cid,
                                                                                   lockmode, erm->noWait);
                                        ReleaseBuffer(buffer);
                                        switch (test)
@@ -1309,8 +1333,7 @@ lnext:    ;
                                                                newSlot = EvalPlanQual(estate,
                                                                                                           erm->rti,
                                                                                                           &update_ctid,
-                                                                                                          update_xmax,
-                                                                                               estate->es_snapshot->curcid);
+                                                                                                          update_xmax);
                                                                if (!TupIsNull(newSlot))
                                                                {
                                                                        slot = planSlot = newSlot;
@@ -1503,7 +1526,7 @@ ExecInsert(TupleTableSlot *slot,
         * t_self field.
         */
        newId = heap_insert(resultRelationDesc, tuple,
-                                               estate->es_snapshot->curcid,
+                                               estate->es_output_cid,
                                                true, true);
 
        IncrAppended();
@@ -1557,8 +1580,7 @@ ExecDelete(ItemPointer tupleid,
        {
                bool            dodelete;
 
-               dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid,
-                                                                               estate->es_snapshot->curcid);
+               dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid);
 
                if (!dodelete)                  /* "do nothing" */
                        return;
@@ -1575,7 +1597,7 @@ ExecDelete(ItemPointer tupleid,
 ldelete:;
        result = heap_delete(resultRelationDesc, tupleid,
                                                 &update_ctid, &update_xmax,
-                                                estate->es_snapshot->curcid,
+                                                estate->es_output_cid,
                                                 estate->es_crosscheck_snapshot,
                                                 true /* wait for commit */ );
        switch (result)
@@ -1599,8 +1621,7 @@ ldelete:;
                                epqslot = EvalPlanQual(estate,
                                                                           resultRelInfo->ri_RangeTableIndex,
                                                                           &update_ctid,
-                                                                          update_xmax,
-                                                                          estate->es_snapshot->curcid);
+                                                                          update_xmax);
                                if (!TupIsNull(epqslot))
                                {
                                        *tupleid = update_ctid;
@@ -1708,8 +1729,7 @@ ExecUpdate(TupleTableSlot *slot,
                HeapTuple       newtuple;
 
                newtuple = ExecBRUpdateTriggers(estate, resultRelInfo,
-                                                                               tupleid, tuple,
-                                                                               estate->es_snapshot->curcid);
+                                                                               tupleid, tuple);
 
                if (newtuple == NULL)   /* "do nothing" */
                        return;
@@ -1755,7 +1775,7 @@ lreplace:;
         */
        result = heap_update(resultRelationDesc, tupleid, tuple,
                                                 &update_ctid, &update_xmax,
-                                                estate->es_snapshot->curcid,
+                                                estate->es_output_cid,
                                                 estate->es_crosscheck_snapshot,
                                                 true /* wait for commit */ );
        switch (result)
@@ -1779,8 +1799,7 @@ lreplace:;
                                epqslot = EvalPlanQual(estate,
                                                                           resultRelInfo->ri_RangeTableIndex,
                                                                           &update_ctid,
-                                                                          update_xmax,
-                                                                          estate->es_snapshot->curcid);
+                                                                          update_xmax);
                                if (!TupIsNull(epqslot))
                                {
                                        *tupleid = update_ctid;
@@ -1973,7 +1992,6 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
  *     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)
@@ -1983,7 +2001,7 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
  */
 TupleTableSlot *
 EvalPlanQual(EState *estate, Index rti,
-                        ItemPointer tid, TransactionId priorXmax, CommandId curCid)
+                        ItemPointer tid, TransactionId priorXmax)
 {
        evalPlanQual *epq;
        EState     *epqstate;
@@ -2063,17 +2081,17 @@ EvalPlanQual(EState *estate, Index rti,
 
                        /*
                         * 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
+                        * cmin against es_output_cid: cmin >= current CID 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)
+                               HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid)
                        {
                                ReleaseBuffer(buffer);
                                return NULL;
@@ -2360,6 +2378,7 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
        epqstate->es_snapshot = estate->es_snapshot;
        epqstate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot;
        epqstate->es_range_table = estate->es_range_table;
+       epqstate->es_output_cid = estate->es_output_cid;
        epqstate->es_result_relations = estate->es_result_relations;
        epqstate->es_num_result_relations = estate->es_num_result_relations;
        epqstate->es_result_relation_info = estate->es_result_relation_info;
@@ -2718,7 +2737,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
 
        heap_insert(estate->es_into_relation_descriptor,
                                tuple,
-                               estate->es_snapshot->curcid,
+                               estate->es_output_cid,
                                estate->es_into_relation_use_wal,
                                false);                 /* never any point in using FSM */
 
index 2f4c8a53053f7e7efcd890bc96288acf4865db2b..5e1387110e4d42171c1b46727ec4eca3d9aec5ba 100644 (file)
@@ -178,6 +178,8 @@ CreateExecutorState(void)
        estate->es_crosscheck_snapshot = InvalidSnapshot;       /* no crosscheck */
        estate->es_range_table = NIL;
 
+       estate->es_output_cid = (CommandId) 0;
+
        estate->es_result_relations = NULL;
        estate->es_num_result_relations = 0;
        estate->es_result_relation_info = NULL;
index 9e6231b32a02e65753d295694ee10cdde57a30c9..8a9fd0b67b85c943620da21ec9fd9b73e35483ba 100644 (file)
@@ -1593,7 +1593,7 @@ _SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
                                         */
                                        ActiveSnapshot = CopySnapshot(snapshot);
                                        if (!read_only)
-                                               ActiveSnapshot->curcid = GetCurrentCommandId();
+                                               ActiveSnapshot->curcid = GetCurrentCommandId(false);
                                }
 
                                if (IsA(stmt, PlannedStmt) &&
index b57abea89ef7101a62368369e1976d60604b7460..410ab2e2603e0b627584fd78e38e4a77bca63f22 100644 (file)
@@ -814,7 +814,7 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
        snapshot->xcnt = count;
        snapshot->subxcnt = subcount;
 
-       snapshot->curcid = GetCurrentCommandId();
+       snapshot->curcid = GetCurrentCommandId(false);
 
        return snapshot;
 }
index 0cd0ee9eadc82c8404410a5c25b77a10ce182652..16bf7d34eb88f9279ccda672b4ba0df4bf0eeb3e 100644 (file)
@@ -402,6 +402,14 @@ RegisterRelcacheInvalidation(Oid dbId, Oid relId)
        AddRelcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
                                                                   dbId, relId);
 
+       /*
+        * Most of the time, relcache invalidation is associated with system
+        * catalog updates, but there are a few cases where it isn't.  Quick
+        * hack to ensure that the next CommandCounterIncrement() will think
+        * that we need to do CommandEndInvalidationMessages().
+        */
+       (void) GetCurrentCommandId(true);
+
        /*
         * If the relation being invalidated is one of those cached in the
         * relcache init file, mark that we need to zap that file at commit.
@@ -420,6 +428,11 @@ RegisterSmgrInvalidation(RelFileNode rnode)
 {
        AddSmgrInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
                                                           rnode);
+
+       /*
+        * As above, just in case there is not an associated catalog change.
+        */
+       (void) GetCurrentCommandId(true);
 }
 
 /*
index 7e0f247906217a63c65a1accbe0ec93aaab53e57..75b5a5a2817356aa1ac8e0a23493b65b438ce1f7 100644 (file)
@@ -382,7 +382,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
                }
                else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
                {
-                       if (HeapTupleHeaderGetCmin(tuple) >= GetCurrentCommandId())
+                       if (HeapTupleHeaderGetCmin(tuple) >= GetCurrentCommandId(false))
                                return false;   /* inserted after scan started */
 
                        if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid */
@@ -401,7 +401,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
                                return true;
                        }
 
-                       if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
+                       if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))
                                return true;    /* deleted after scan started */
                        else
                                return false;   /* deleted before scan started */
@@ -443,7 +443,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
        {
                if (tuple->t_infomask & HEAP_IS_LOCKED)
                        return true;
-               if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
+               if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))
                        return true;            /* deleted after scan started */
                else
                        return false;           /* deleted before scan started */
index 0075433bb14239639aa2f9ca451c339ef52fea44..b3f8a5d51e1244a4865d2d428932713bdcce4d36 100644 (file)
@@ -143,7 +143,7 @@ extern TransactionId GetTopTransactionIdIfAny(void);
 extern TransactionId GetCurrentTransactionId(void);
 extern TransactionId GetCurrentTransactionIdIfAny(void);
 extern SubTransactionId GetCurrentSubTransactionId(void);
-extern CommandId GetCurrentCommandId(void);
+extern CommandId GetCurrentCommandId(bool used);
 extern TimestampTz GetCurrentTransactionStartTimestamp(void);
 extern TimestampTz GetCurrentStatementStartTimestamp(void);
 extern TimestampTz GetCurrentTransactionStopTimestamp(void);
index 0e100c303f7183bf72b9e32deaeb91ba6353d910..376165504be8b9bff6d651098034e6bef9efcea8 100644 (file)
@@ -124,8 +124,7 @@ extern void ExecASDeleteTriggers(EState *estate,
                                         ResultRelInfo *relinfo);
 extern bool ExecBRDeleteTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
-                                        ItemPointer tupleid,
-                                        CommandId cid);
+                                        ItemPointer tupleid);
 extern void ExecARDeleteTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         ItemPointer tupleid);
@@ -136,8 +135,7 @@ extern void ExecASUpdateTriggers(EState *estate,
 extern HeapTuple ExecBRUpdateTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         ItemPointer tupleid,
-                                        HeapTuple newtuple,
-                                        CommandId cid);
+                                        HeapTuple newtuple);
 extern void ExecARUpdateTriggers(EState *estate,
                                         ResultRelInfo *relinfo,
                                         ItemPointer tupleid,
index b10c81f1cc0f881cbfab8870c67998ff500342a5..4476db685d4448345473021e808d3bd2d432ea1f 100644 (file)
@@ -143,7 +143,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, CommandId curCid);
+                        ItemPointer tid, TransactionId priorXmax);
 extern PlanState *ExecGetActivePlanTree(QueryDesc *queryDesc);
 extern DestReceiver *CreateIntoRelDestReceiver(void);
 
index 952f4b38e151e2dff62f7500f41019c33efe726c..e7665ce75930eb32bb25ceadc5187f50f724eb76 100644 (file)
@@ -311,6 +311,9 @@ typedef struct EState
        Snapshot        es_crosscheck_snapshot; /* crosscheck time qual for RI */
        List       *es_range_table; /* List of RangeTblEntry */
 
+       /* If query can insert/delete tuples, the command ID to mark them with */
+       CommandId       es_output_cid;
+
        /* Info about target table for insert/update/delete queries: */
        ResultRelInfo *es_result_relations; /* array of ResultRelInfos */
        int                     es_num_result_relations;                /* length of array */
index 14e45fe4893417f60ac5e33695ea2a8fcfa585ff..b63894c2837ef96093217d706e8aac88a3195871 100644 (file)
@@ -4,66 +4,16 @@
 CREATE TEMP TABLE combocidtest (foobar int);
 BEGIN;
 -- a few dummy ops to push up the CommandId counter
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
 INSERT INTO combocidtest VALUES (1);
 INSERT INTO combocidtest VALUES (2);
 SELECT ctid,cmin,* FROM combocidtest;
@@ -79,8 +29,8 @@ UPDATE combocidtest SET foobar = foobar + 10;
 SELECT ctid,cmin,* FROM combocidtest;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,3) |   13 |     11
- (0,4) |   13 |     12
+ (0,3) |   12 |     11
+ (0,4) |   12 |     12
 (2 rows)
 
 ROLLBACK TO s1;
@@ -109,8 +59,8 @@ DELETE FROM combocidtest;
 FETCH ALL FROM c;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,1) |    2 |      1
- (0,2) |    2 |      2
+ (0,1) |    1 |      1
+ (0,2) |    1 |      2
  (0,5) |    0 |    333
 (3 rows)
 
@@ -118,79 +68,29 @@ ROLLBACK;
 SELECT ctid,cmin,* FROM combocidtest;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,1) |    2 |      1
- (0,2) |    2 |      2
+ (0,1) |    1 |      1
+ (0,2) |    1 |      2
 (2 rows)
 
 -- check behavior with locked tuples
 BEGIN;
 -- a few dummy ops to push up the CommandId counter
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
-SELECT 1;
- ?column? 
-----------
-        1
-(1 row)
-
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
 INSERT INTO combocidtest VALUES (444);
 SELECT ctid,cmin,* FROM combocidtest;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,1) |    2 |      1
- (0,2) |    2 |      2
+ (0,1) |    1 |      1
+ (0,2) |    1 |      2
  (0,6) |   10 |    444
 (3 rows)
 
@@ -199,16 +99,16 @@ SAVEPOINT s1;
 SELECT ctid,cmin,* FROM combocidtest FOR UPDATE;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,1) |    2 |      1
- (0,2) |    2 |      2
+ (0,1) |    1 |      1
+ (0,2) |    1 |      2
  (0,6) |   10 |    444
 (3 rows)
 
 SELECT ctid,cmin,* FROM combocidtest;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,1) |    2 |      1
- (0,2) |    2 |      2
+ (0,1) |    1 |      1
+ (0,2) |    1 |      2
  (0,6) |   10 |    444
 (3 rows)
 
@@ -217,17 +117,17 @@ UPDATE combocidtest SET foobar = foobar + 10;
 SELECT ctid,cmin,* FROM combocidtest;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,7) |   14 |     11
- (0,8) |   14 |     12
- (0,9) |   14 |    454
+ (0,7) |   12 |     11
+ (0,8) |   12 |     12
+ (0,9) |   12 |    454
 (3 rows)
 
 ROLLBACK TO s1;
 SELECT ctid,cmin,* FROM combocidtest;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,1) |   14 |      1
- (0,2) |   14 |      2
+ (0,1) |   12 |      1
+ (0,2) |   12 |      2
  (0,6) |    0 |    444
 (3 rows)
 
@@ -235,8 +135,8 @@ COMMIT;
 SELECT ctid,cmin,* FROM combocidtest;
  ctid  | cmin | foobar 
 -------+------+--------
- (0,1) |   14 |      1
- (0,2) |   14 |      2
+ (0,1) |   12 |      1
+ (0,2) |   12 |      2
  (0,6) |    0 |    444
 (3 rows)
 
index 3f30839b1f6be38a1f7047de53190d15e816bc20..f24ac6b01a1b5f796aba6f00894444835e752f92 100644 (file)
@@ -6,16 +6,16 @@ CREATE TEMP TABLE combocidtest (foobar int);
 BEGIN;
 
 -- a few dummy ops to push up the CommandId counter
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
 
 INSERT INTO combocidtest VALUES (1);
 INSERT INTO combocidtest VALUES (2);
@@ -58,16 +58,16 @@ SELECT ctid,cmin,* FROM combocidtest;
 BEGIN;
 
 -- a few dummy ops to push up the CommandId counter
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
-SELECT 1;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
+INSERT INTO combocidtest SELECT 1 LIMIT 0;
 
 INSERT INTO combocidtest VALUES (444);