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.
/*
- * $PostgreSQL: pgsql/contrib/pgrowlocks/pgrowlocks.c,v 1.7 2007/08/28 22:59:30 tgl Exp $
+ * $PostgreSQL: pgsql/contrib/pgrowlocks/pgrowlocks.c,v 1.8 2007/11/30 21:22:53 tgl Exp $
*
* Copyright (c) 2005-2006 Tatsuo Ishii
*
/* 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;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.245 2007/11/15 21:14:32 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.246 2007/11/30 21:22:53 tgl Exp $
*
*
* INTERFACE ROUTINES
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);
}
/*
result = heap_delete(relation, tid,
&update_ctid, &update_xmax,
- GetCurrentCommandId(), InvalidSnapshot,
+ GetCurrentCommandId(true), InvalidSnapshot,
true /* wait for commit */ );
switch (result)
{
result = heap_update(relation, otid, tup,
&update_ctid, &update_xmax,
- GetCurrentCommandId(), InvalidSnapshot,
+ GetCurrentCommandId(true), InvalidSnapshot,
true /* wait for commit */ );
switch (result)
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.79 2007/11/15 21:14:32 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.80 2007/11/30 21:22:53 tgl Exp $
*
*
* INTERFACE ROUTINES
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
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.253 2007/11/15 21:14:32 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.254 2007/11/30 21:22:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
static SubTransactionId currentSubTransactionId;
static CommandId currentCommandId;
+static bool currentCommandIdUsed;
/*
* xactStartTimestamp is the value of transaction_timestamp().
/*
* 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;
}
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();
}
s->subTransactionId = TopSubTransactionId;
currentSubTransactionId = TopSubTransactionId;
currentCommandId = FirstCommandId;
+ currentCommandIdUsed = false;
/*
* must initialize resource-management stuff first
/* 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))));
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.136 2007/04/12 06:53:46 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.137 2007/11/30 21:22:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
result = heap_update(lRel, &lTuple->t_self, rTuple,
&update_ctid, &update_xmax,
- GetCurrentCommandId(), InvalidSnapshot,
+ GetCurrentCommandId(true),
+ InvalidSnapshot,
false /* no wait for commit */ );
switch (result)
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.288 2007/11/15 21:14:33 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.289 2007/11/30 21:22:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* 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);
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 */
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.167 2007/11/15 22:25:15 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.168 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* 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,
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.224 2007/11/16 01:51:22 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.225 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static HeapTuple GetTupleForTrigger(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tid,
- CommandId cid,
TupleTableSlot **newSlot);
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
int tgindx,
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];
TupleTableSlot *newSlot;
int i;
- trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, cid, &newSlot);
+ trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
if (trigtuple == NULL)
return false;
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);
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];
TupleTableSlot *newSlot;
int i;
- trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, cid, &newSlot);
+ trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
if (trigtuple == NULL)
return NULL;
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);
static HeapTuple
GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
- ItemPointer tid, CommandId cid,
+ ItemPointer tid,
TupleTableSlot **newSlot)
{
Relation relation = relinfo->ri_RelationDesc;
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)
{
epqslot = EvalPlanQual(estate,
relinfo->ri_RangeTableIndex,
&update_ctid,
- update_xmax,
- cid);
+ update_xmax);
if (!TupIsNull(epqslot))
{
*tid = update_ctid;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.300 2007/11/15 22:25:15 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.301 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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
*/
estate->es_snapshot = queryDesc->snapshot;
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)
newSlot = EvalPlanQual(estate,
erm->rti,
&update_ctid,
- update_xmax,
- estate->es_snapshot->curcid);
+ update_xmax);
if (!TupIsNull(newSlot))
{
slot = planSlot = newSlot;
* t_self field.
*/
newId = heap_insert(resultRelationDesc, tuple,
- estate->es_snapshot->curcid,
+ estate->es_output_cid,
true, true);
IncrAppended();
{
bool dodelete;
- dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid,
- estate->es_snapshot->curcid);
+ dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid);
if (!dodelete) /* "do nothing" */
return;
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)
epqslot = EvalPlanQual(estate,
resultRelInfo->ri_RangeTableIndex,
&update_ctid,
- update_xmax,
- estate->es_snapshot->curcid);
+ update_xmax);
if (!TupIsNull(epqslot))
{
*tupleid = update_ctid;
HeapTuple newtuple;
newtuple = ExecBRUpdateTriggers(estate, resultRelInfo,
- tupleid, tuple,
- estate->es_snapshot->curcid);
+ tupleid, tuple);
if (newtuple == NULL) /* "do nothing" */
return;
*/
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)
epqslot = EvalPlanQual(estate,
resultRelInfo->ri_RangeTableIndex,
&update_ctid,
- update_xmax,
- estate->es_snapshot->curcid);
+ update_xmax);
if (!TupIsNull(epqslot))
{
*tupleid = update_ctid;
* 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)
*/
TupleTableSlot *
EvalPlanQual(EState *estate, Index rti,
- ItemPointer tid, TransactionId priorXmax, CommandId curCid)
+ ItemPointer tid, TransactionId priorXmax)
{
evalPlanQual *epq;
EState *epqstate;
/*
* 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;
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;
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 */
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.152 2007/11/15 21:14:34 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.153 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.185 2007/11/30 18:38:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.186 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
ActiveSnapshot = CopySnapshot(snapshot);
if (!read_only)
- ActiveSnapshot->curcid = GetCurrentCommandId();
+ ActiveSnapshot->curcid = GetCurrentCommandId(false);
}
if (IsA(stmt, PlannedStmt) &&
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.37 2007/11/15 21:14:38 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.38 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
snapshot->xcnt = count;
snapshot->subxcnt = subcount;
- snapshot->curcid = GetCurrentCommandId();
+ snapshot->curcid = GetCurrentCommandId(false);
return snapshot;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.81 2007/11/15 21:14:39 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.82 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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.
*/
{
AddSmgrInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
rnode);
+
+ /*
+ * As above, just in case there is not an associated catalog change.
+ */
+ (void) GetCurrentCommandId(true);
}
/*
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.107 2007/11/15 21:14:41 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.108 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
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 */
return true;
}
- if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
+ if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))
return true; /* deleted after scan started */
else
return false; /* deleted before scan started */
{
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 */
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/xact.h,v 1.91 2007/11/15 21:14:42 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/xact.h,v 1.92 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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);
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.63 2007/11/15 21:14:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.64 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
ResultRelInfo *relinfo);
extern bool ExecBRDeleteTriggers(EState *estate,
ResultRelInfo *relinfo,
- ItemPointer tupleid,
- CommandId cid);
+ ItemPointer tupleid);
extern void ExecARDeleteTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid);
extern HeapTuple ExecBRUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple newtuple,
- CommandId cid);
+ HeapTuple newtuple);
extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.144 2007/11/15 22:25:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.145 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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);
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.181 2007/11/15 22:25:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.182 2007/11/30 21:22:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
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 */
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;
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;
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)
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)
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)
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)
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)
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);
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);