]> granicus.if.org Git - postgresql/commitdiff
Clean up the representation of special snapshots by including a "method
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 25 Mar 2007 19:45:14 +0000 (19:45 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 25 Mar 2007 19:45:14 +0000 (19:45 +0000)
pointer" in every Snapshot struct.  This allows removal of the case-by-case
tests in HeapTupleSatisfiesVisibility, which should make it a bit faster
(I didn't try any performance tests though).  More importantly, we are no
longer violating portable C practices by assuming that small integers are
distinct from all pointer values, and HeapTupleSatisfiesDirty no longer
has a non-reentrant API involving side-effects on a global variable.

There were a couple of places calling HeapTupleSatisfiesXXX routines
directly rather than through the HeapTupleSatisfiesVisibility macro.
Since these places had to be changed anyway, I chose to make them go
through the macro for uniformity.

Along the way I renamed HeapTupleSatisfiesSnapshot to HeapTupleSatisfiesMVCC
to emphasize that it's only used with MVCC-type snapshots.  I was sorely
tempted to rename HeapTupleSatisfiesVisibility to HeapTupleSatisfiesSnapshot,
but forebore for the moment to avoid confusion and reduce the likelihood that
this patch breaks some of the pending patches.  Might want to reconsider
doing that later.

contrib/pgstattuple/pgstattuple.c
src/backend/access/heap/heapam.c
src/backend/access/nbtree/nbtinsert.c
src/backend/catalog/catalog.c
src/backend/catalog/index.c
src/backend/executor/execMain.c
src/backend/storage/ipc/procarray.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/time/tqual.c
src/include/utils/tqual.h

index 5bac70949d098a48d0cd5cb1e5a37b0bf39ff5c1..d08156ad851730f2cc84bb1fdec54cededb83a30 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.25 2006/10/04 00:29:46 momjian Exp $
+ * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.26 2007/03/25 19:45:13 tgl Exp $
  *
  * Copyright (c) 2001,2002     Tatsuo Ishii
  *
@@ -256,10 +256,10 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
        /* scan the relation */
        while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
        {
-               /* must hold a buffer lock to call HeapTupleSatisfiesNow */
+               /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
                LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
 
-               if (HeapTupleSatisfiesNow(tuple->t_data, scan->rs_cbuf))
+               if (HeapTupleSatisfiesVisibility(tuple, SnapshotNow, scan->rs_cbuf))
                {
                        stat.tuple_len += tuple->t_len;
                        stat.tuple_count++;
index d46308863fe2c34d450e5f810bb04a9339daa725..367831a515a8b9943002ffa280e09bee7175b4ca 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.228 2007/02/09 03:35:33 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.229 2007/03/25 19:45:13 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1706,7 +1706,7 @@ l1:
        if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
        {
                /* Perform additional check for serializable RI updates */
-               if (!HeapTupleSatisfiesSnapshot(tp.t_data, crosscheck, buffer))
+               if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
                        result = HeapTupleUpdated;
        }
 
@@ -2025,7 +2025,7 @@ l2:
        if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
        {
                /* Perform additional check for serializable RI updates */
-               if (!HeapTupleSatisfiesSnapshot(oldtup.t_data, crosscheck, buffer))
+               if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
                        result = HeapTupleUpdated;
        }
 
index d2992374e9766a54ce81428ea337e2bd6bbafdc1..c1671ce333c766b49c91b732f61d29f9f83eecff 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.154 2007/03/05 14:13:12 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.155 2007/03/25 19:45:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -176,11 +176,14 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
 {
        TupleDesc       itupdesc = RelationGetDescr(rel);
        int                     natts = rel->rd_rel->relnatts;
+       SnapshotData SnapshotDirty;
        OffsetNumber maxoff;
        Page            page;
        BTPageOpaque opaque;
        Buffer          nbuf = InvalidBuffer;
 
+       InitDirtySnapshot(SnapshotDirty);
+
        page = BufferGetPage(buf);
        opaque = (BTPageOpaque) PageGetSpecialPointer(page);
        maxoff = PageGetMaxOffsetNumber(page);
@@ -232,13 +235,13 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
                                /* okay, we gotta fetch the heap tuple ... */
                                curitup = (IndexTuple) PageGetItem(page, curitemid);
                                htup.t_self = curitup->t_tid;
-                               if (heap_fetch(heapRel, SnapshotDirty, &htup, &hbuffer,
+                               if (heap_fetch(heapRel, &SnapshotDirty, &htup, &hbuffer,
                                                           true, NULL))
                                {
                                        /* it is a duplicate */
                                        TransactionId xwait =
-                                       (TransactionIdIsValid(SnapshotDirty->xmin)) ?
-                                       SnapshotDirty->xmin : SnapshotDirty->xmax;
+                                       (TransactionIdIsValid(SnapshotDirty.xmin)) ?
+                                       SnapshotDirty.xmin : SnapshotDirty.xmax;
 
                                        ReleaseBuffer(hbuffer);
 
index 4d4a851130bcb56ba39401025d71dda5aed5b067..049d216a7877a4128f76285e0c6651e00baed4be 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.69 2007/01/05 22:19:24 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/catalog.c,v 1.70 2007/03/25 19:45:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -361,10 +361,13 @@ Oid
 GetNewOidWithIndex(Relation relation, Relation indexrel)
 {
        Oid                     newOid;
+       SnapshotData SnapshotDirty;
        IndexScanDesc scan;
        ScanKeyData key;
        bool            collides;
 
+       InitDirtySnapshot(SnapshotDirty);
+
        /* Generate new OIDs until we find one not in the table */
        do
        {
@@ -377,7 +380,7 @@ GetNewOidWithIndex(Relation relation, Relation indexrel)
 
                /* see notes above about using SnapshotDirty */
                scan = index_beginscan(relation, indexrel,
-                                                          SnapshotDirty, 1, &key);
+                                                          &SnapshotDirty, 1, &key);
 
                collides = HeapTupleIsValid(index_getnext(scan, ForwardScanDirection));
 
index 092a8d8de385d078afc3dc52fc57ca17081321bc..b660a94aabc4a04772c0204ec48dabfac1694671 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.280 2007/03/03 20:08:41 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.281 2007/03/25 19:45:14 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1828,10 +1828,11 @@ validate_index_heapscan(Relation heapRelation,
                         */
                        if (indexInfo->ii_Unique)
                        {
-                               /* must hold a buffer lock to call HeapTupleSatisfiesNow */
+                               /* must lock buffer to call HeapTupleSatisfiesVisibility */
                                LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
 
-                               if (HeapTupleSatisfiesNow(heapTuple->t_data, scan->rs_cbuf))
+                               if (HeapTupleSatisfiesVisibility(heapTuple, SnapshotNow,
+                                                                                                scan->rs_cbuf))
                                        check_unique = true;
                                else
                                        check_unique = false;
index 239656dd06b945f477f8c1c471f8555da7be1dc9..b2f7159e8c0099a76e065fb02c092be7cf8a62e5 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.290 2007/03/06 02:06:13 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.291 2007/03/25 19:45:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1893,6 +1893,7 @@ EvalPlanQual(EState *estate, Index rti,
        Relation        relation;
        HeapTupleData tuple;
        HeapTuple       copyTuple = NULL;
+       SnapshotData SnapshotDirty;
        bool            endNode;
 
        Assert(rti != 0);
@@ -1925,12 +1926,13 @@ EvalPlanQual(EState *estate, Index rti,
         *
         * Loop here to deal with updated or busy tuples
         */
+       InitDirtySnapshot(SnapshotDirty);
        tuple.t_self = *tid;
        for (;;)
        {
                Buffer          buffer;
 
-               if (heap_fetch(relation, SnapshotDirty, &tuple, &buffer, true, NULL))
+               if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL))
                {
                        /*
                         * If xmin isn't what we're expecting, the slot must have been
@@ -1948,17 +1950,17 @@ EvalPlanQual(EState *estate, Index rti,
                        }
 
                        /* otherwise xmin should not be dirty... */
-                       if (TransactionIdIsValid(SnapshotDirty->xmin))
+                       if (TransactionIdIsValid(SnapshotDirty.xmin))
                                elog(ERROR, "t_xmin is uncommitted in tuple to be updated");
 
                        /*
                         * If tuple is being updated by other transaction then we have to
                         * wait for its commit/abort.
                         */
-                       if (TransactionIdIsValid(SnapshotDirty->xmax))
+                       if (TransactionIdIsValid(SnapshotDirty.xmax))
                        {
                                ReleaseBuffer(buffer);
-                               XactLockTableWait(SnapshotDirty->xmax);
+                               XactLockTableWait(SnapshotDirty.xmax);
                                continue;               /* loop back to repeat heap_fetch */
                        }
 
index d2d39770c5b3d1fa363d8142c4395a7f89352d03..12e61c89263566372d9136dcb06eff198b785add 100644 (file)
@@ -23,7 +23,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.22 2007/03/23 03:16:39 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.23 2007/03/25 19:45:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -484,7 +484,7 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
  * limited cache area for subxact XIDs, full information may not be
  * available.  If we find any overflowed subxid arrays, we have to mark
  * the snapshot's subxid data as overflowed, and extra work will need to
- * be done to determine what's running (see XidInSnapshot() in tqual.c).
+ * be done to determine what's running (see XidInMVCCSnapshot() in tqual.c).
  *
  * We also update the following backend-global variables:
  *             TransactionXmin: the oldest xmin of any snapshot in use in the
index 72200ced9dcc0527d79fa596da8a6c9809953afe..af363f4acff393b5c6ec795392d33625c087c82b 100644 (file)
@@ -15,7 +15,7 @@
  *
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.92 2007/03/15 23:12:06 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.93 2007/03/25 19:45:14 tgl Exp $
  *
  * ----------
  */
@@ -277,11 +277,11 @@ RI_FKey_check(PG_FUNCTION_ARGS)
         * We should not even consider checking the row if it is no longer valid,
         * since it was either deleted (so the deferred check should be skipped)
         * or updated (in which case only the latest version of the row should be
-        * checked).  Test its liveness with HeapTupleSatisfiesItself.
+        * checked).  Test its liveness according to SnapshotSelf.
         *
         * NOTE: The normal coding rule is that one must acquire the buffer
-        * content lock to call HeapTupleSatisfiesFOO.  We can skip that here
-        * because we know that AfterTriggerExecute just fetched the tuple
+        * content lock to call HeapTupleSatisfiesVisibility.  We can skip that
+        * here because we know that AfterTriggerExecute just fetched the tuple
         * successfully, so there cannot be a VACUUM compaction in progress on the
         * page (either heap_fetch would have waited for the VACUUM, or the
         * VACUUM's LockBufferForCleanup would be waiting for us to drop pin).
@@ -289,7 +289,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
         * can be entitled to change its xmin/xmax.
         */
        Assert(new_row_buf != InvalidBuffer);
-       if (!HeapTupleSatisfiesItself(new_row->t_data, new_row_buf))
+       if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
                return PointerGetDatum(NULL);
 
        /*
index 13cc211b5dbe808883a29c171c343967d4eaf8c0..429005a843a343dc24c63b10013cb155f8048856 100644 (file)
@@ -1,15 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * tqual.c
- *       POSTGRES "time" qualification code, ie, tuple visibility rules.
- *
- * The caller must hold at least a shared buffer context lock on the buffer
- * containing the tuple.  (VACUUM FULL assumes it's sufficient to have
- * exclusive lock on the containing relation, instead.)
+ *       POSTGRES "time qualification" code, ie, tuple visibility rules.
  *
  * NOTE: all the HeapTupleSatisfies routines will update the tuple's
  * "hint" status bits if we see that the inserting or deleting transaction
- * has now committed or aborted.
+ * has now committed or aborted.  If the hint bits are changed,
+ * SetBufferCommitInfoNeedsSave is called on the passed-in buffer.
+ * The caller must hold at least a shared buffer context lock on the buffer
+ * containing the tuple.
  *
  * NOTE: must check TransactionIdIsInProgress (which looks in PGPROC array)
  * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
@@ -32,7 +31,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.101 2007/01/05 22:19:47 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.102 2007/03/25 19:45:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "storage/procarray.h"
 #include "utils/tqual.h"
 
+
+/* Static variables representing various special snapshot semantics */
+SnapshotData SnapshotNowData = {HeapTupleSatisfiesNow};
+SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
+SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
+SnapshotData SnapshotToastData = {HeapTupleSatisfiesToast};
+
 /*
  * These SnapshotData structs are static to simplify memory allocation
  * (see the hack in GetSnapshotData to avoid repeated malloc/free).
  */
-static SnapshotData SnapshotDirtyData;
-static SnapshotData SerializableSnapshotData;
-static SnapshotData LatestSnapshotData;
+static SnapshotData SerializableSnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData LatestSnapshotData = {HeapTupleSatisfiesMVCC};
 
 /* Externally visible pointers to valid snapshots: */
-Snapshot       SnapshotDirty = &SnapshotDirtyData;
 Snapshot       SerializableSnapshot = NULL;
 Snapshot       LatestSnapshot = NULL;
 
@@ -73,11 +77,11 @@ TransactionId RecentXmin = InvalidTransactionId;
 TransactionId RecentGlobalXmin = InvalidTransactionId;
 
 /* local functions */
-static bool XidInSnapshot(TransactionId xid, Snapshot snapshot);
+static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
 
 
 /*
- * HeapTupleSatisfiesItself
+ * HeapTupleSatisfiesSelf
  *             True iff heap tuple is valid "for itself".
  *
  *     Here, we consider the effects of:
@@ -101,7 +105,7 @@ static bool XidInSnapshot(TransactionId xid, Snapshot snapshot);
  *                      Xmax is not committed)))                       that has not been committed
  */
 bool
-HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer)
+HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
 {
        if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
        {
@@ -278,7 +282,7 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer)
  *             that do catalog accesses.  this is unfortunate, but not critical.
  */
 bool
-HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer)
+HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
 {
        if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
        {
@@ -422,6 +426,16 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer)
        return false;
 }
 
+/*
+ * HeapTupleSatisfiesAny
+ *             Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
+ */
+bool
+HeapTupleSatisfiesAny(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
+{
+       return true;
+}
+
 /*
  * HeapTupleSatisfiesToast
  *             True iff heap tuple is valid as a TOAST row.
@@ -437,7 +451,8 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer)
  * table.
  */
 bool
-HeapTupleSatisfiesToast(HeapTupleHeader tuple, Buffer buffer)
+HeapTupleSatisfiesToast(HeapTupleHeader tuple, Snapshot snapshot,
+                                               Buffer buffer)
 {
        if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
        {
@@ -676,20 +691,22 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
  *             previous commands of this transaction
  *             changes made by the current command
  *
- * This is essentially like HeapTupleSatisfiesItself as far as effects of
+ * This is essentially like HeapTupleSatisfiesSelf as far as effects of
  * the current transaction and committed/aborted xacts are concerned.
  * However, we also include the effects of other xacts still in progress.
  *
- * Returns extra information in the global variable SnapshotDirty, namely
- * xids of concurrent xacts that affected the tuple.  SnapshotDirty->xmin
- * is set to InvalidTransactionId if xmin is either committed good or
- * committed dead; or to xmin if that transaction is still in progress.
- * Similarly for SnapshotDirty->xmax.
+ * A special hack is that the passed-in snapshot struct is used as an
+ * output argument to return the xids of concurrent xacts that affected the
+ * tuple.  snapshot->xmin is set to the tuple's xmin if that is another
+ * transaction that's still in progress; or to InvalidTransactionId if the
+ * tuple's xmin is committed good, committed dead, or my own xact.  Similarly
+ * for snapshot->xmax and the tuple's xmax.
  */
 bool
-HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
+HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot,
+                                               Buffer buffer)
 {
-       SnapshotDirty->xmin = SnapshotDirty->xmax = InvalidTransactionId;
+       snapshot->xmin = snapshot->xmax = InvalidTransactionId;
 
        if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
        {
@@ -759,7 +776,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
                }
                else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
                {
-                       SnapshotDirty->xmin = HeapTupleHeaderGetXmin(tuple);
+                       snapshot->xmin = HeapTupleHeaderGetXmin(tuple);
                        /* XXX shouldn't we fall through to look at xmax? */
                        return true;            /* in insertion by other */
                }
@@ -805,7 +822,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
 
        if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
        {
-               SnapshotDirty->xmax = HeapTupleHeaderGetXmax(tuple);
+               snapshot->xmax = HeapTupleHeaderGetXmax(tuple);
                return true;
        }
 
@@ -832,8 +849,8 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
 }
 
 /*
- * HeapTupleSatisfiesSnapshot
- *             True iff heap tuple is valid for the given snapshot.
+ * HeapTupleSatisfiesMVCC
+ *             True iff heap tuple is valid for the given MVCC snapshot.
  *
  *     Here, we consider the effects of:
  *             all transactions committed as of the time of the given snapshot
@@ -853,8 +870,8 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
  * can't see it.)
  */
 bool
-HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot,
-                                                  Buffer buffer)
+HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot,
+                                          Buffer buffer)
 {
        if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
        {
@@ -949,7 +966,7 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot,
         * By here, the inserting transaction has committed - have to check
         * when...
         */
-       if (XidInSnapshot(HeapTupleHeaderGetXmin(tuple), snapshot))
+       if (XidInMVCCSnapshot(HeapTupleHeaderGetXmin(tuple), snapshot))
                return false;                   /* treat as still in progress */
 
        if (tuple->t_infomask & HEAP_XMAX_INVALID)      /* xid invalid or aborted */
@@ -994,7 +1011,7 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot,
        /*
         * OK, the deleting transaction committed too ... but when?
         */
-       if (XidInSnapshot(HeapTupleHeaderGetXmax(tuple), snapshot))
+       if (XidInMVCCSnapshot(HeapTupleHeaderGetXmax(tuple), snapshot))
                return true;                    /* treat as still in progress */
 
        return false;
@@ -1241,8 +1258,6 @@ GetLatestSnapshot(void)
  *             Copy the given snapshot.
  *
  * The copy is palloc'd in the current memory context.
- *
- * Note that this will not work on "special" snapshots.
  */
 Snapshot
 CopySnapshot(Snapshot snapshot)
@@ -1290,7 +1305,7 @@ CopySnapshot(Snapshot snapshot)
  * This is currently identical to pfree, but is provided for cleanliness.
  *
  * Do *not* apply this to the results of GetTransactionSnapshot or
- * GetLatestSnapshot.
+ * GetLatestSnapshot, since those are just static structs.
  */
 void
 FreeSnapshot(Snapshot snapshot)
@@ -1316,7 +1331,7 @@ FreeXactSnapshot(void)
 }
 
 /*
- * XidInSnapshot
+ * XidInMVCCSnapshot
  *             Is the given XID still-in-progress according to the snapshot?
  *
  * Note: GetSnapshotData never stores either top xid or subxids of our own
@@ -1325,7 +1340,7 @@ FreeXactSnapshot(void)
  * apply this for known-committed XIDs.
  */
 static bool
-XidInSnapshot(TransactionId xid, Snapshot snapshot)
+XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
 {
        uint32          i;
 
index 0b3ef50ac344c6de6e914e6a866a394e52f527e4..e3596e2d507148fd76f59fbe88dcbc9dc966882d 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.65 2007/01/05 22:19:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.66 2007/03/25 19:45:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 
 /*
- * "Regular" snapshots are pointers to a SnapshotData structure.
- *
- * We also have some "special" snapshot values that have fixed meanings
- * and don't need any backing SnapshotData.  These are encoded by small
- * integer values, which of course is a gross violation of ANSI C, but
- * it works fine on all known platforms.
- *
- * SnapshotDirty is an even more special case: its semantics are fixed,
- * but there is a backing SnapshotData struct for it.  That struct is
- * actually used as *output* data from tqual.c, not input into it.
- * (But hey, SnapshotDirty ought to have a dirty implementation, no? ;-))
+ * We use SnapshotData structures to represent both "regular" (MVCC)
+ * snapshots and "special" snapshots that have non-MVCC semantics.
+ * The specific semantics of a snapshot are encoded by the "satisfies"
+ * function.
  */
+typedef struct SnapshotData *Snapshot;
+
+typedef bool (*SnapshotSatisfiesFunc) (HeapTupleHeader tuple,
+                                                                          Snapshot snapshot, Buffer buffer);
 
 typedef struct SnapshotData
 {
-       TransactionId xmin;                     /* XID < xmin are visible to me */
-       TransactionId xmax;                     /* XID >= xmax are invisible to me */
+       SnapshotSatisfiesFunc satisfies;        /* tuple test function */
+       /*
+        * The remaining fields are used only for MVCC snapshots, and are
+        * normally just zeroes in special snapshots.  (But xmin and xmax
+        * are used specially by HeapTupleSatisfiesDirty.)
+        *
+        * An MVCC snapshot can never see the effects of XIDs >= xmax.
+        * It can see the effects of all older XIDs except those listed in
+        * the snapshot.  xmin is stored as an optimization to avoid needing
+        * to search the XID arrays for most tuples.
+        */
+       TransactionId xmin;                     /* all XID < xmin are visible to me */
+       TransactionId xmax;                     /* all XID >= xmax are invisible to me */
        uint32          xcnt;                   /* # of xact ids in xip[] */
        TransactionId *xip;                     /* array of xact IDs in progress */
        /* note: all ids in xip[] satisfy xmin <= xip[i] < xmax */
@@ -50,24 +58,30 @@ typedef struct SnapshotData
        CommandId       curcid;                 /* in my xact, CID < curcid are visible */
 } SnapshotData;
 
-typedef SnapshotData *Snapshot;
+#define InvalidSnapshot                ((Snapshot) NULL)
 
-/* Special snapshot values: */
-#define InvalidSnapshot                                ((Snapshot) 0x0)        /* same as NULL */
-#define SnapshotNow                                    ((Snapshot) 0x1)
-#define SnapshotSelf                           ((Snapshot) 0x2)
-#define SnapshotAny                                    ((Snapshot) 0x3)
-#define SnapshotToast                          ((Snapshot) 0x4)
+/* Static variables representing various special snapshot semantics */
+extern DLLIMPORT SnapshotData SnapshotNowData;
+extern DLLIMPORT SnapshotData SnapshotSelfData;
+extern DLLIMPORT SnapshotData SnapshotAnyData;
+extern DLLIMPORT SnapshotData SnapshotToastData;
 
-extern DLLIMPORT Snapshot SnapshotDirty;
+#define SnapshotNow                    (&SnapshotNowData)
+#define SnapshotSelf           (&SnapshotSelfData)
+#define SnapshotAny                    (&SnapshotAnyData)
+#define SnapshotToast          (&SnapshotToastData)
+
+/*
+ * We don't provide a static SnapshotDirty variable because it would be
+ * non-reentrant.  Instead, users of that snapshot type should declare a
+ * local variable of type SnapshotData, and initialize it with this macro.
+ */
+#define InitDirtySnapshot(snapshotdata)  \
+       ((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
 
 /* This macro encodes the knowledge of which snapshots are MVCC-safe */
 #define IsMVCCSnapshot(snapshot)  \
-       ((snapshot) != SnapshotNow && \
-        (snapshot) != SnapshotSelf && \
-        (snapshot) != SnapshotAny && \
-        (snapshot) != SnapshotToast && \
-        (snapshot) != SnapshotDirty)
+       ((snapshot)->satisfies == HeapTupleSatisfiesMVCC)
 
 
 extern DLLIMPORT Snapshot SerializableSnapshot;
@@ -78,7 +92,6 @@ extern TransactionId TransactionXmin;
 extern TransactionId RecentXmin;
 extern TransactionId RecentGlobalXmin;
 
-
 /*
  * HeapTupleSatisfiesVisibility
  *             True iff heap tuple satisfies a time qual.
@@ -86,30 +99,11 @@ extern TransactionId RecentGlobalXmin;
  * Notes:
  *     Assumes heap tuple is valid.
  *     Beware of multiple evaluations of snapshot argument.
- *     Hint bits in the HeapTuple's t_infomask may be updated as a side effect.
+ *     Hint bits in the HeapTuple's t_infomask may be updated as a side effect;
+ *     if so, the indicated buffer is marked dirty.
  */
 #define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \
-((snapshot) == SnapshotNow ? \
-       HeapTupleSatisfiesNow((tuple)->t_data, buffer) \
-: \
-       ((snapshot) == SnapshotSelf ? \
-               HeapTupleSatisfiesItself((tuple)->t_data, buffer) \
-       : \
-               ((snapshot) == SnapshotAny ? \
-                       true \
-               : \
-                       ((snapshot) == SnapshotToast ? \
-                               HeapTupleSatisfiesToast((tuple)->t_data, buffer) \
-                       : \
-                               ((snapshot) == SnapshotDirty ? \
-                                       HeapTupleSatisfiesDirty((tuple)->t_data, buffer) \
-                               : \
-                                       HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot, buffer) \
-                               ) \
-                       ) \
-               ) \
-       ) \
-)
+       ((*(snapshot)->satisfies) ((tuple)->t_data, snapshot, buffer))
 
 /* Result codes for HeapTupleSatisfiesUpdate */
 typedef enum
@@ -127,16 +121,25 @@ typedef enum
        HEAPTUPLE_DEAD,                         /* tuple is dead and deletable */
        HEAPTUPLE_LIVE,                         /* tuple is live (committed, no deleter) */
        HEAPTUPLE_RECENTLY_DEAD,        /* tuple is dead, but not deletable yet */
-       HEAPTUPLE_INSERT_IN_PROGRESS,           /* inserting xact is still in progress */
+       HEAPTUPLE_INSERT_IN_PROGRESS,   /* inserting xact is still in progress */
        HEAPTUPLE_DELETE_IN_PROGRESS    /* deleting xact is still in progress */
 } HTSV_Result;
 
-extern bool HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer);
-extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer);
-extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer);
-extern bool HeapTupleSatisfiesToast(HeapTupleHeader tuple, Buffer buffer);
-extern bool HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple,
-                                                  Snapshot snapshot, Buffer buffer);
+/* These are the "satisfies" test routines for the various snapshot types */
+extern bool HeapTupleSatisfiesMVCC(HeapTupleHeader tuple,
+                                                                  Snapshot snapshot, Buffer buffer);
+extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple,
+                                                                 Snapshot snapshot, Buffer buffer);
+extern bool HeapTupleSatisfiesSelf(HeapTupleHeader tuple,
+                                                                  Snapshot snapshot, Buffer buffer);
+extern bool HeapTupleSatisfiesAny(HeapTupleHeader tuple,
+                                                                 Snapshot snapshot, Buffer buffer);
+extern bool HeapTupleSatisfiesToast(HeapTupleHeader tuple,
+                                                                       Snapshot snapshot, Buffer buffer);
+extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple,
+                                                                       Snapshot snapshot, Buffer buffer);
+
+/* Special "satisfies" routines with different APIs */
 extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTupleHeader tuple,
                                                 CommandId curcid, Buffer buffer);
 extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTupleHeader tuple,