]> granicus.if.org Git - postgresql/commitdiff
Combine cmin and cmax fields of HeapTupleHeaders into a single field, by
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 9 Feb 2007 03:35:35 +0000 (03:35 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 9 Feb 2007 03:35:35 +0000 (03:35 +0000)
keeping private state in each backend that has inserted and deleted the same
tuple during its current top-level transaction.  This is sufficient since
there is no need to be able to determine the cmin/cmax from any other
transaction.  This gets us back down to 23-byte headers, removing a penalty
paid in 8.0 to support subtransactions.  Patch by Heikki Linnakangas, with
minor revisions by moi, following a design hashed out awhile back on the
pghackers list.

21 files changed:
src/backend/access/common/heaptuple.c
src/backend/access/heap/heapam.c
src/backend/access/transam/xact.c
src/backend/utils/fmgr/fmgr.c
src/backend/utils/time/Makefile
src/backend/utils/time/combocid.c [new file with mode: 0644]
src/include/access/htup.h
src/include/catalog/catversion.h
src/include/storage/bufpage.h
src/include/utils/combocid.h [new file with mode: 0644]
src/pl/plperl/plperl.c
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/plpgsql.h
src/pl/plpython/plpython.c
src/pl/tcl/pltcl.c
src/test/regress/expected/combocid.out [new file with mode: 0644]
src/test/regress/expected/without_oid.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/combocid.sql [new file with mode: 0644]
src/test/regress/sql/without_oid.sql

index 6200f953ebcf5a58726c234e2c07d9f1830dd7bb..470f48777de3ee733dfb2ff0d1c4b507ae515137 100644 (file)
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.114 2007/01/09 22:00:59 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.115 2007/02/09 03:35:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -582,14 +582,18 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
                case MinTransactionIdAttributeNumber:
                        result = TransactionIdGetDatum(HeapTupleHeaderGetXmin(tup->t_data));
                        break;
-               case MinCommandIdAttributeNumber:
-                       result = CommandIdGetDatum(HeapTupleHeaderGetCmin(tup->t_data));
-                       break;
                case MaxTransactionIdAttributeNumber:
                        result = TransactionIdGetDatum(HeapTupleHeaderGetXmax(tup->t_data));
                        break;
+               case MinCommandIdAttributeNumber:
                case MaxCommandIdAttributeNumber:
-                       result = CommandIdGetDatum(HeapTupleHeaderGetCmax(tup->t_data));
+                       /*
+                        * cmin and cmax are now both aliases for the same field,
+                        * which can in fact also be a combo command id.  XXX perhaps we
+                        * should return the "real" cmin or cmax if possible, that is
+                        * if we are inside the originating transaction?
+                        */
+                       result = CommandIdGetDatum(HeapTupleHeaderGetRawCommandId(tup->t_data));
                        break;
                case TableOidAttributeNumber:
                        result = ObjectIdGetDatum(tup->t_tableOid);
index 71ba6d2e52ef25d94b00820e2803fa24b9d21751..d46308863fe2c34d450e5f810bb04a9339daa725 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.227 2007/02/05 04:22:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.228 2007/02/09 03:35:33 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1407,8 +1407,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
        tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
        HeapTupleHeaderSetXmin(tup->t_data, xid);
        HeapTupleHeaderSetCmin(tup->t_data, cid);
-       HeapTupleHeaderSetXmax(tup->t_data, 0);         /* zero out Datum fields */
-       HeapTupleHeaderSetCmax(tup->t_data, 0);         /* for cleanliness */
+       HeapTupleHeaderSetXmax(tup->t_data, 0);         /* for cleanliness */
        tup->t_tableOid = RelationGetRelid(relation);
 
        /*
@@ -1585,6 +1584,7 @@ heap_delete(Relation relation, ItemPointer tid,
        PageHeader      dp;
        Buffer          buffer;
        bool            have_tuple_lock = false;
+       bool            iscombo;
 
        Assert(ItemPointerIsValid(tid));
 
@@ -1724,6 +1724,9 @@ l1:
                return result;
        }
 
+       /* replace cid with a combo cid if necessary */
+       HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
+
        START_CRIT_SECTION();
 
        /* store transaction information of xact deleting the tuple */
@@ -1733,7 +1736,7 @@ l1:
                                                           HEAP_IS_LOCKED |
                                                           HEAP_MOVED);
        HeapTupleHeaderSetXmax(tp.t_data, xid);
-       HeapTupleHeaderSetCmax(tp.t_data, cid);
+       HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
        /* Make sure there is no forward chain link in t_ctid */
        tp.t_data->t_ctid = tp.t_self;
 
@@ -1893,6 +1896,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
        Size            newtupsize,
                                pagefree;
        bool            have_tuple_lock = false;
+       bool            iscombo;
 
        Assert(ItemPointerIsValid(otid));
 
@@ -2058,8 +2062,13 @@ l2:
        newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
        HeapTupleHeaderSetXmin(newtup->t_data, xid);
        HeapTupleHeaderSetCmin(newtup->t_data, cid);
-       HeapTupleHeaderSetXmax(newtup->t_data, 0);      /* zero out Datum fields */
-       HeapTupleHeaderSetCmax(newtup->t_data, 0);      /* for cleanliness */
+       HeapTupleHeaderSetXmax(newtup->t_data, 0);      /* for cleanliness */
+
+       /*
+        * Replace cid with a combo cid if necessary.  Note that we already put
+        * the plain cid into the new tuple.
+        */
+       HeapTupleHeaderAdjustCmax(oldtup.t_data, &cid, &iscombo);
 
        /*
         * If the toaster needs to be activated, OR if the new tuple will not fit
@@ -2088,7 +2097,7 @@ l2:
                                                                           HEAP_IS_LOCKED |
                                                                           HEAP_MOVED);
                HeapTupleHeaderSetXmax(oldtup.t_data, xid);
-               HeapTupleHeaderSetCmax(oldtup.t_data, cid);
+               HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
                /* temporarily make it look not-updated */
                oldtup.t_data->t_ctid = oldtup.t_self;
                already_marked = true;
@@ -2183,7 +2192,7 @@ l2:
                                                                           HEAP_IS_LOCKED |
                                                                           HEAP_MOVED);
                HeapTupleHeaderSetXmax(oldtup.t_data, xid);
-               HeapTupleHeaderSetCmax(oldtup.t_data, cid);
+               HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
        }
 
        /* record address of new tuple in t_ctid of old one */
@@ -2687,12 +2696,11 @@ l3:
        /*
         * Store transaction information of xact locking the tuple.
         *
-        * Note: our CID is meaningless if storing a MultiXactId, but no harm in
-        * storing it anyway.
+        * Note: Cmax is meaningless in this context, so don't set it; this
+        * avoids possibly generating a useless combo CID.
         */
        tuple->t_data->t_infomask = new_infomask;
        HeapTupleHeaderSetXmax(tuple->t_data, xid);
-       HeapTupleHeaderSetCmax(tuple->t_data, cid);
        /* Make sure there is no forward chain link in t_ctid */
        tuple->t_data->t_ctid = *tid;
 
@@ -3443,7 +3451,7 @@ heap_xlog_delete(XLogRecPtr lsn, XLogRecord *record)
                                                  HEAP_IS_LOCKED |
                                                  HEAP_MOVED);
        HeapTupleHeaderSetXmax(htup, record->xl_xid);
-       HeapTupleHeaderSetCmax(htup, FirstCommandId);
+       HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
        /* Make sure there is no forward chain link in t_ctid */
        htup->t_ctid = xlrec->target.tid;
        PageSetLSN(page, lsn);
@@ -3608,7 +3616,7 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool move)
                                                          HEAP_IS_LOCKED |
                                                          HEAP_MOVED);
                HeapTupleHeaderSetXmax(htup, record->xl_xid);
-               HeapTupleHeaderSetCmax(htup, FirstCommandId);
+               HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
                /* Set forward chain link in t_ctid */
                htup->t_ctid = xlrec->newtid;
        }
@@ -3761,7 +3769,7 @@ heap_xlog_lock(XLogRecPtr lsn, XLogRecord *record)
        else
                htup->t_infomask |= HEAP_XMAX_EXCL_LOCK;
        HeapTupleHeaderSetXmax(htup, xlrec->locking_xid);
-       HeapTupleHeaderSetCmax(htup, FirstCommandId);
+       HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
        /* Make sure there is no forward chain link in t_ctid */
        htup->t_ctid = xlrec->target.tid;
        PageSetLSN(page, lsn);
index 26a3c5ddc2ccd69d777183e93bbc0798111880af..ab8b5fd8b4ec8de39e977a3b5c700fb83b51016f 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.233 2007/02/07 23:11:29 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.234 2007/02/09 03:35:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "storage/lmgr.h"
 #include "storage/procarray.h"
 #include "storage/smgr.h"
+#include "utils/combocid.h"
 #include "utils/flatfiles.h"
+#include "utils/guc.h"
 #include "utils/inval.h"
 #include "utils/memutils.h"
 #include "utils/relcache.h"
-#include "utils/guc.h"
 
 
 /*
@@ -1628,6 +1629,7 @@ CommitTransaction(void)
        AtEOXact_Namespace(true);
        /* smgrcommit already done */
        AtEOXact_Files();
+       AtEOXact_ComboCid();
        pgstat_clear_snapshot();
        pgstat_count_xact_commit();
        pgstat_report_txn_timestamp(0);
@@ -1845,6 +1847,7 @@ PrepareTransaction(void)
        AtEOXact_Namespace(true);
        /* smgrcommit already done */
        AtEOXact_Files();
+       AtEOXact_ComboCid();
        pgstat_clear_snapshot();
 
        CurrentResourceOwner = NULL;
@@ -1997,6 +2000,7 @@ AbortTransaction(void)
        AtEOXact_Namespace(false);
        smgrabort();
        AtEOXact_Files();
+       AtEOXact_ComboCid();
        pgstat_clear_snapshot();
        pgstat_count_xact_rollback();
        pgstat_report_txn_timestamp(0);
index 886883a6aec33b2fffac9d775ed6d3f8497895ba..c36d9fee13450e32f29bf9680e2a22ab801df3d7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.103 2007/01/05 22:19:43 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.104 2007/02/09 03:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,7 +64,7 @@ typedef struct
        /* fn_oid is the hash key and so must be first! */
        Oid                     fn_oid;                 /* OID of an external C function */
        TransactionId fn_xmin;          /* for checking up-to-dateness */
-       CommandId       fn_cmin;
+       ItemPointerData fn_tid;
        PGFunction      user_fn;                /* the function's address */
        const Pg_finfo_record *inforec;         /* address of its info record */
 } CFuncHashTabEntry;
@@ -483,7 +483,7 @@ lookup_C_func(HeapTuple procedureTuple)
        if (entry == NULL)
                return NULL;                    /* no such entry */
        if (entry->fn_xmin == HeapTupleHeaderGetXmin(procedureTuple->t_data) &&
-               entry->fn_cmin == HeapTupleHeaderGetCmin(procedureTuple->t_data))
+               ItemPointerEquals(&entry->fn_tid, &procedureTuple->t_self))
                return entry;                   /* OK */
        return NULL;                            /* entry is out of date */
 }
@@ -521,7 +521,7 @@ record_C_func(HeapTuple procedureTuple,
                                        &found);
        /* OID is already filled in */
        entry->fn_xmin = HeapTupleHeaderGetXmin(procedureTuple->t_data);
-       entry->fn_cmin = HeapTupleHeaderGetCmin(procedureTuple->t_data);
+       entry->fn_tid = procedureTuple->t_self;
        entry->user_fn = user_fn;
        entry->inforec = inforec;
 }
index 67d2bfd262648242b6426553520f3de0aab1c637..9cbe31f8a6beda97d7efe55dc3920d852fc9f11f 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for utils/time
 #
 # IDENTIFICATION
-#    $PostgreSQL: pgsql/src/backend/utils/time/Makefile,v 1.11 2007/01/20 17:16:15 petere Exp $
+#    $PostgreSQL: pgsql/src/backend/utils/time/Makefile,v 1.12 2007/02/09 03:35:34 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -12,12 +12,12 @@ subdir = src/backend/utils/time
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = tqual.o
+OBJS = combocid.o tqual.o
 
 all: SUBSYS.o
 
 SUBSYS.o: $(OBJS)
        $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS)
 
-clean: 
+clean:
        rm -f SUBSYS.o $(OBJS)
diff --git a/src/backend/utils/time/combocid.c b/src/backend/utils/time/combocid.c
new file mode 100644 (file)
index 0000000..5ba7666
--- /dev/null
@@ -0,0 +1,282 @@
+/*-------------------------------------------------------------------------
+ *
+ * combocid.c
+ *       Combo command ID support routines
+ *
+ * Before version 8.3, HeapTupleHeaderData had separate fields for cmin
+ * and cmax.  To reduce the header size, cmin and cmax are now overlayed
+ * in the same field in the header.  That usually works because you rarely
+ * insert and delete a tuple in the same transaction, and we don't need
+ * either field to remain valid after the originating transaction exits.
+ * To make it work when the inserting transaction does delete the tuple,
+ * we create a "combo" command ID and store that in the tuple header
+ * instead of cmin and cmax. The combo command ID can be mapped to the
+ * real cmin and cmax using a backend-private array, which is managed by
+ * this module.
+ *
+ * To allow reusing existing combo cids, we also keep a hash table that
+ * maps cmin,cmax pairs to combo cids.  This keeps the data structure size
+ * reasonable in most cases, since the number of unique pairs used by any
+ * one transaction is likely to be small.
+ *
+ * With a 32-bit combo command id we can represent 2^32 distinct cmin,cmax
+ * combinations. In the most perverse case where each command deletes a tuple
+ * generated by every previous command, the number of combo command ids
+ * required for N commands is N*(N+1)/2.  That means that in the worst case,
+ * that's enough for 92682 commands.  In practice, you'll run out of memory
+ * and/or disk space way before you reach that limit.
+ *
+ * The array and hash table are kept in TopTransactionContext, and are
+ * destroyed at the end of each transaction.
+ *
+ *
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       $PostgreSQL: pgsql/src/backend/utils/time/combocid.c,v 1.1 2007/02/09 03:35:34 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup.h"
+#include "access/xact.h"
+#include "utils/combocid.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+
+
+/* Hash table to lookup combo cids by cmin and cmax */
+static HTAB *comboHash = NULL;
+
+/* Key and entry structures for the hash table */
+typedef struct
+{
+       CommandId cmin;
+       CommandId cmax;
+} ComboCidKeyData;
+
+typedef ComboCidKeyData *ComboCidKey;
+
+typedef struct
+{
+       ComboCidKeyData key;
+       CommandId combocid;
+} ComboCidEntryData;
+
+typedef ComboCidEntryData *ComboCidEntry;
+
+/* Initial size of the hash table */
+#define CCID_HASH_SIZE                 100
+
+
+/*
+ * An array of cmin,cmax pairs, indexed by combo command id.
+ * To convert a combo cid to cmin and cmax, you do a simple array lookup.
+ */
+static ComboCidKey comboCids = NULL;
+static int usedComboCids = 0;                  /* number of elements in comboCids */
+static int sizeComboCids = 0;                  /* allocated size of array */
+
+/* Initial size of the array */
+#define CCID_ARRAY_SIZE                        100
+
+
+/* prototypes for internal functions */
+static CommandId GetComboCommandId(CommandId cmin, CommandId cmax);
+static CommandId GetRealCmin(CommandId combocid);
+static CommandId GetRealCmax(CommandId combocid);
+
+
+/**** External API ****/
+
+/*
+ * GetCmin and GetCmax assert that they are only called in situations where
+ * they make sense, that is, can deliver a useful answer.  If you have
+ * reason to examine a tuple's t_cid field from a transaction other than
+ * the originating one, use HeapTupleHeaderGetRawCommandId() directly.
+ */
+
+CommandId
+HeapTupleHeaderGetCmin(HeapTupleHeader tup)
+{
+       CommandId cid = HeapTupleHeaderGetRawCommandId(tup);
+
+       Assert(!(tup->t_infomask & HEAP_MOVED));
+       Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tup)));
+
+       if (tup->t_infomask & HEAP_COMBOCID)
+               return GetRealCmin(cid);
+       else
+               return cid;
+}
+
+CommandId
+HeapTupleHeaderGetCmax(HeapTupleHeader tup)
+{
+       CommandId cid = HeapTupleHeaderGetRawCommandId(tup);
+
+       /* We do not store cmax when locking a tuple */
+       Assert(!(tup->t_infomask & (HEAP_MOVED | HEAP_IS_LOCKED)));
+       Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tup)));
+
+       if (tup->t_infomask & HEAP_COMBOCID)
+               return GetRealCmax(cid);
+       else
+               return cid;
+}
+
+/*
+ * Given a tuple we are about to delete, determine the correct value to store
+ * into its t_cid field.
+ *
+ * If we don't need a combo CID, *cmax is unchanged and *iscombo is set to
+ * FALSE.  If we do need one, *cmax is replaced by a combo CID and *iscombo
+ * is set to TRUE.
+ *
+ * The reason this is separate from the actual HeapTupleHeaderSetCmax()
+ * operation is that this could fail due to out-of-memory conditions.  Hence
+ * we need to do this before entering the critical section that actually
+ * changes the tuple in shared buffers.
+ */
+void
+HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
+                                                 CommandId *cmax,
+                                                 bool *iscombo)
+{
+       /*
+        * If we're marking a tuple deleted that was inserted by (any
+        * subtransaction of) our transaction, we need to use a combo command id.
+        * Test for HEAP_XMIN_COMMITTED first, because it's cheaper than a
+        * TransactionIdIsCurrentTransactionId call.
+        */
+       if (!(tup->t_infomask & HEAP_XMIN_COMMITTED) &&
+               TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tup)))
+       {
+               CommandId cmin = HeapTupleHeaderGetRawCommandId(tup);
+
+               *cmax = GetComboCommandId(cmin, *cmax);
+               *iscombo = true;
+       }
+       else
+       {
+               *iscombo = false;
+       }
+}
+
+/*
+ * Combo command ids are only interesting to the inserting and deleting
+ * transaction, so we can forget about them at the end of transaction.
+ */
+void
+AtEOXact_ComboCid(void)
+{
+       /*
+        * Don't bother to pfree. These are allocated in TopTransactionContext,
+        * so they're going to go away at the end of transaction anyway.
+        */
+       comboHash = NULL;
+
+       comboCids = NULL;
+       usedComboCids = 0;
+       sizeComboCids = 0;
+}
+
+
+/**** Internal routines ****/
+
+/*
+ * Get a combo command id that maps to cmin and cmax.
+ *
+ * We try to reuse old combo command ids when possible.
+ */
+static CommandId
+GetComboCommandId(CommandId cmin, CommandId cmax)
+{
+       CommandId combocid;
+       ComboCidKeyData key;
+       ComboCidEntry entry;
+       bool found;
+
+       /*
+        * Create the hash table and array the first time we need to use
+        * combo cids in the transaction.
+        */
+       if (comboHash == NULL)
+       {
+               HASHCTL hash_ctl;
+
+               memset(&hash_ctl, 0, sizeof(hash_ctl));
+               hash_ctl.keysize = sizeof(ComboCidKeyData);
+               hash_ctl.entrysize = sizeof(ComboCidEntryData);
+               hash_ctl.hash = tag_hash;
+               hash_ctl.hcxt = TopTransactionContext;
+
+               comboHash = hash_create("Combo CIDs",
+                                                               CCID_HASH_SIZE,
+                                                               &hash_ctl,
+                                                               HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+
+               comboCids = (ComboCidKeyData *)
+                       MemoryContextAlloc(TopTransactionContext,
+                                                          sizeof(ComboCidKeyData) * CCID_ARRAY_SIZE);
+               sizeComboCids = CCID_ARRAY_SIZE;
+               usedComboCids = 0;
+       }
+
+       /* Lookup or create a hash entry with the desired cmin/cmax */
+
+       /* We assume there is no struct padding in ComboCidKeyData! */
+       key.cmin = cmin;
+       key.cmax = cmax;
+       entry = (ComboCidEntry) hash_search(comboHash,
+                                                                               (void *) &key,
+                                                                               HASH_ENTER,
+                                                                               &found);
+
+       if (found)
+       {
+               /* Reuse an existing combo cid */
+               return entry->combocid;
+       }
+
+       /*
+        * We have to create a new combo cid. Check that there's room
+        * for it in the array, and grow it if there isn't.
+        */
+       if (usedComboCids >= sizeComboCids)
+       {
+               /* We need to grow the array */
+               int             newsize = sizeComboCids * 2;
+
+               comboCids = (ComboCidKeyData *)
+                       repalloc(comboCids, sizeof(ComboCidKeyData) * newsize);
+               sizeComboCids = newsize;
+       }
+
+       combocid = usedComboCids;
+
+       comboCids[combocid].cmin = cmin;
+       comboCids[combocid].cmax = cmax;
+       usedComboCids++;
+
+       entry->combocid = combocid;
+
+       return combocid;
+}
+
+static CommandId
+GetRealCmin(CommandId combocid)
+{
+       Assert(combocid < usedComboCids);
+       return comboCids[combocid].cmin;
+}
+
+static CommandId
+GetRealCmax(CommandId combocid)
+{
+       Assert(combocid < usedComboCids);
+       return comboCids[combocid].cmax;
+}
index eecc6959e89a424187216d4e94eb5e10301450af..31de835d2900aa9523d5b8e229f218e2e132acf6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.90 2007/02/05 04:22:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.91 2007/02/09 03:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,7 @@
 
 /*
  * Heap tuple header.  To avoid wasting space, the fields should be
- * layed out in such a way to avoid structure padding.
+ * laid out in such a way as to avoid structure padding.
  *
  * Datums of composite types (row types) share the same general structure
  * as on-disk tuples, so that the same routines can be used to build and
  *                     object ID (if HEAP_HASOID is set in t_infomask)
  *                     user data fields
  *
- * We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac in four
- * physical fields.  Xmin, Cmin and Xmax are always really stored, but
- * Cmax and Xvac share a field.  This works because we know that there are
- * only a limited number of states that a tuple can be in, and that Cmax
- * is only interesting for the lifetime of the deleting transaction.
- * This assumes that VACUUM FULL never tries to move a tuple whose Cmax
- * is still interesting (ie, delete-in-progress).
- *
- * Note that in 7.3 and 7.4 a similar idea was applied to Xmax and Cmin.
- * However, with the advent of subtransactions, a tuple may need both Xmax
- * and Cmin simultaneously, so this is no longer possible.
+ * We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac in three
+ * physical fields.  Xmin and Xmax are always really stored, but Cmin, Cmax
+ * and Xvac share a field.  This works because we know that Cmin and Cmax
+ * are only interesting for the lifetime of the inserting and deleting
+ * transaction respectively.  If a tuple is inserted and deleted in the same
+ * transaction, we store a "combo" command id that can be mapped to the real
+ * cmin and cmax, but only by use of local state within the originating
+ * backend.  See combocid.c for more details.  Meanwhile, Xvac is only set
+ * by VACUUM FULL, which does not have any command sub-structure and so does
+ * not need either Cmin or Cmax.  (This requires that VACUUM FULL never try
+ * to move a tuple whose Cmin or Cmax is still interesting, ie, an insert-
+ * in-progress or delete-in-progress tuple.)
  *
  * A word about t_ctid: whenever a new tuple is stored on disk, its t_ctid
  * is initialized with its own TID (location). If the tuple is ever updated,
 typedef struct HeapTupleFields
 {
        TransactionId t_xmin;           /* inserting xact ID */
-       CommandId       t_cmin;                 /* inserting command ID */
        TransactionId t_xmax;           /* deleting or locking xact ID */
 
        union
        {
-               CommandId       t_cmax;         /* deleting or locking command ID */
+               CommandId       t_cid;          /* inserting or deleting command ID, or both */
                TransactionId t_xvac;   /* VACUUM FULL xact ID */
-       }                       t_field4;
+       }                       t_field3;
 } HeapTupleFields;
 
 typedef struct DatumTupleFields
@@ -145,7 +145,7 @@ typedef struct HeapTupleHeaderData
 
        uint8           t_hoff;                 /* sizeof header incl. bitmap, padding */
 
-       /* ^ - 27 bytes - ^ */
+       /* ^ - 23 bytes - ^ */
 
        bits8           t_bits[1];              /* bitmap of NULLs -- VARIABLE LENGTH */
 
@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
 #define HEAP_HASCOMPRESSED             0x0008  /* has compressed stored attribute(s) */
 #define HEAP_HASEXTENDED               0x000C  /* the two above combined */
 #define HEAP_HASOID                            0x0010  /* has an object-id field */
-/* 0x0020 is presently unused */
+#define HEAP_COMBOCID                  0x0020  /* t_cid is a combo cid */
 #define HEAP_XMAX_EXCL_LOCK            0x0040  /* xmax is exclusive locker */
 #define HEAP_XMAX_SHARED_LOCK  0x0080  /* xmax is shared locker */
 /* if either LOCK bit is set, xmax hasn't deleted the tuple, only locked it */
@@ -180,17 +180,13 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
                                                                                 * FULL */
 #define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN)
 
-#define HEAP_XACT_MASK                 0xFFC0  /* visibility-related bits */
+#define HEAP_XACT_MASK                 0xFFE0  /* visibility-related bits */
 
-/* information stored in t_infomask2, and accessor macros */
+/*
+ * information stored in t_infomask2:
+ */
 #define HEAP_NATTS_MASK                        0x7FF   /* 11 bits for number of attributes */
-/* bits 0xF800 are unused */
-
-#define HeapTupleHeaderGetNatts(tup) ((tup)->t_infomask2 & HEAP_NATTS_MASK)
-#define HeapTupleHeaderSetNatts(tup, natts) \
-( \
-       (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \
-)
+/* bits 0xF800 are currently unused */
 
 /*
  * HeapTupleHeader accessor macros
@@ -219,39 +215,40 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
        TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_xmax) \
 )
 
-#define HeapTupleHeaderGetCmin(tup) \
-( \
-       (tup)->t_choice.t_heap.t_cmin \
-)
-
-#define HeapTupleHeaderSetCmin(tup, cid) \
-( \
-       (tup)->t_choice.t_heap.t_cmin = (cid) \
-)
-
 /*
- * Note: GetCmax will produce wrong answers after SetXvac has been executed
- * by a transaction other than the inserting one.  We could check
- * HEAP_XMAX_INVALID and return FirstCommandId if it's clear, but since that
- * bit will be set again if the deleting transaction aborts, there'd be no
- * real gain in safety from the extra test.  So, just rely on the caller not
- * to trust the value unless it's meaningful.
+ * HeapTupleHeaderGetRawCommandId will give you what's in the header whether
+ * it is useful or not.  Most code should use HeapTupleHeaderGetCmin or
+ * HeapTupleHeaderGetCmax instead, but note that those Assert that you can
+ * get a legitimate result, ie you are in the originating transaction!
  */
-#define HeapTupleHeaderGetCmax(tup) \
+#define HeapTupleHeaderGetRawCommandId(tup) \
 ( \
-       (tup)->t_choice.t_heap.t_field4.t_cmax \
+       (tup)->t_choice.t_heap.t_field3.t_cid \
 )
 
-#define HeapTupleHeaderSetCmax(tup, cid) \
+/* SetCmin is reasonably simple since we never need a combo CID */
+#define HeapTupleHeaderSetCmin(tup, cid) \
 do { \
        Assert(!((tup)->t_infomask & HEAP_MOVED)); \
-       (tup)->t_choice.t_heap.t_field4.t_cmax = (cid); \
+       (tup)->t_choice.t_heap.t_field3.t_cid = (cid); \
+       (tup)->t_infomask &= ~HEAP_COMBOCID; \
+} while (0)
+
+/* SetCmax must be used after HeapTupleHeaderAdjustCmax; see combocid.c */
+#define HeapTupleHeaderSetCmax(tup, cid, iscombo) \
+do { \
+       Assert(!((tup)->t_infomask & HEAP_MOVED)); \
+       (tup)->t_choice.t_heap.t_field3.t_cid = (cid); \
+       if (iscombo) \
+               (tup)->t_infomask |= HEAP_COMBOCID; \
+       else \
+               (tup)->t_infomask &= ~HEAP_COMBOCID; \
 } while (0)
 
 #define HeapTupleHeaderGetXvac(tup) \
 ( \
        ((tup)->t_infomask & HEAP_MOVED) ? \
-               (tup)->t_choice.t_heap.t_field4.t_xvac \
+               (tup)->t_choice.t_heap.t_field3.t_xvac \
        : \
                InvalidTransactionId \
 )
@@ -259,7 +256,7 @@ do { \
 #define HeapTupleHeaderSetXvac(tup, xid) \
 do { \
        Assert((tup)->t_infomask & HEAP_MOVED); \
-       TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_field4.t_xvac); \
+       TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_field3.t_xvac); \
 } while (0)
 
 #define HeapTupleHeaderGetDatumLength(tup) \
@@ -306,6 +303,14 @@ do { \
        *((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid); \
 } while (0)
 
+#define HeapTupleHeaderGetNatts(tup) \
+       ((tup)->t_infomask2 & HEAP_NATTS_MASK)
+
+#define HeapTupleHeaderSetNatts(tup, natts) \
+( \
+       (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \
+)
+
 
 /*
  * BITMAPLEN(NATTS) -
@@ -374,9 +379,9 @@ do { \
  * and thereby prevent accidental use of the nonexistent fields.
  *
  * MinimalTupleData contains a length word, some padding, and fields matching
- * HeapTupleHeaderData beginning with t_infomask2. The padding is chosen so that
- * offsetof(t_infomask2) is the same modulo MAXIMUM_ALIGNOF in both structs.
- * This makes data alignment rules equivalent in both cases.
+ * HeapTupleHeaderData beginning with t_infomask2. The padding is chosen so
+ * that offsetof(t_infomask2) is the same modulo MAXIMUM_ALIGNOF in both
+ * structs.   This makes data alignment rules equivalent in both cases.
  *
  * When a minimal tuple is accessed via a HeapTupleData pointer, t_data is
  * set to point MINIMAL_TUPLE_OFFSET bytes before the actual start of the
@@ -405,7 +410,7 @@ typedef struct MinimalTupleData
 
        uint8           t_hoff;                 /* sizeof header incl. bitmap, padding */
 
-       /* ^ - 27 bytes - ^ */
+       /* ^ - 23 bytes - ^ */
 
        bits8           t_bits[1];              /* bitmap of NULLs -- VARIABLE LENGTH */
 
@@ -638,4 +643,11 @@ typedef struct xl_heap_freeze
 
 #define SizeOfHeapFreeze (offsetof(xl_heap_freeze, cutoff_xid) + sizeof(TransactionId))
 
+/* HeapTupleHeader functions implemented in utils/time/combocid.c */
+extern CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup);
+extern CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup);
+extern void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
+                                                                         CommandId *cmax,
+                                                                         bool *iscombo);
+
 #endif   /* HTUP_H */
index 9ebc4e9b9fda65397b9b6ac1cd6defef8ce218e5..5bdda05960ebd49a39cf933b076cd0d04b7b3dc9 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.382 2007/02/07 23:11:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.383 2007/02/09 03:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200702071
+#define CATALOG_VERSION_NO     200702081
 
 #endif
index 53ce47f0689da1258209eb8b857b073d64b51eb1..abb51404a54855b71fb8bca100e06c3270e8c08f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.69 2007/01/05 22:19:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/storage/bufpage.h,v 1.70 2007/02/09 03:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -132,10 +132,11 @@ typedef PageHeaderData *PageHeader;
 /*
  * Page layout version number 0 is for pre-7.3 Postgres releases.
  * Releases 7.3 and 7.4 use 1, denoting a new HeapTupleHeader layout.
- * Release 8.0 changed the HeapTupleHeader layout again.
- * Release 8.1 redefined HeapTupleHeader infomask bits.
+ * Release 8.0 uses 2; it changed the HeapTupleHeader layout again.
+ * Release 8.1 uses 3; it redefined HeapTupleHeader infomask bits.
+ * Release 8.3 uses 4; it changed the HeapTupleHeader layout again.
  */
-#define PG_PAGE_LAYOUT_VERSION         3
+#define PG_PAGE_LAYOUT_VERSION         4
 
 
 /* ----------------------------------------------------------------
diff --git a/src/include/utils/combocid.h b/src/include/utils/combocid.h
new file mode 100644 (file)
index 0000000..b9497a6
--- /dev/null
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * combocid.h
+ *       Combo command ID support routines
+ *
+ *
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/utils/combocid.h,v 1.1 2007/02/09 03:35:34 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef COMBOCID_H
+#define COMBOCID_H
+
+/*
+ * HeapTupleHeaderGetCmin and HeapTupleHeaderGetCmax function prototypes
+ * are in access/htup.h, because that's where the macro definitions that
+ * those functions replaced used to be.
+ */
+
+extern void AtEOXact_ComboCid(void);
+
+#endif   /* COMBOCID_H */
index 0cf8c57a137fe7a63210bba00ba73690388bc444..33027c53bde1dfdd20da2eaf3e8da6617f2020d3 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plperl.c - perl as a procedural language for PostgreSQL
  *
- *       $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.126 2007/02/01 19:10:29 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.127 2007/02/09 03:35:34 tgl Exp $
  *
  **********************************************************************/
 
@@ -41,7 +41,7 @@ typedef struct plperl_proc_desc
 {
        char       *proname;
        TransactionId fn_xmin;
-       CommandId       fn_cmin;
+       ItemPointerData fn_tid;
        bool            fn_readonly;
        bool            lanpltrusted;
        bool            fn_retistuple;  /* true, if function returns tuple */
@@ -296,7 +296,7 @@ _PG_init(void)
  *
  * We start out by creating a "held" interpreter that we can use in
  * trusted or untrusted mode (but not both) as the need arises. Later, we
- * assign that interpreter if it is available to either the trusted or 
+ * assign that interpreter if it is available to either the trusted or
  * untrusted interpreter. If it has already been assigned, and we need to
  * create the other interpreter, we do that if we can, or error out.
  * We detect if it is safe to run two interpreters during the setup of the
@@ -304,7 +304,7 @@ _PG_init(void)
  */
 
 
-static void 
+static void
 check_interp(bool trusted)
 {
        if (interp_state == INTERP_HELD)
@@ -322,7 +322,7 @@ check_interp(bool trusted)
                plperl_held_interp = NULL;
                trusted_context = trusted;
        }
-       else if (interp_state == INTERP_BOTH || 
+       else if (interp_state == INTERP_BOTH ||
                         (trusted && interp_state == INTERP_TRUSTED) ||
                         (!trusted && interp_state == INTERP_UNTRUSTED))
        {
@@ -349,11 +349,9 @@ check_interp(bool trusted)
        }
        else
        {
-               elog(ERROR, 
+               elog(ERROR,
                         "cannot allocate second Perl interpreter on this platform");
-
        }
-       
 }
 
 
@@ -425,7 +423,7 @@ plperl_init_interp(void)
                elog(ERROR, "could not allocate Perl interpreter");
 
        perl_construct(plperl_held_interp);
-       perl_parse(plperl_held_interp, plperl_init_shared_libs, 
+       perl_parse(plperl_held_interp, plperl_init_shared_libs,
                           3, embedding, NULL);
        perl_run(plperl_held_interp);
 
@@ -434,7 +432,7 @@ plperl_init_interp(void)
                SV *res;
 
                res = eval_pv(TEST_FOR_MULTI,TRUE);
-               can_run_two = SvIV(res); 
+               can_run_two = SvIV(res);
                interp_state = INTERP_HELD;
        }
 
@@ -1430,7 +1428,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
        /************************************************************
         * Lookup the internal proc name in the hashtable
         ************************************************************/
-       hash_entry = hash_search(plperl_proc_hash, internal_proname, 
+       hash_entry = hash_search(plperl_proc_hash, internal_proname,
                                                         HASH_FIND, NULL);
 
        if (hash_entry)
@@ -1445,7 +1443,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
                 * function's pg_proc entry without changing its OID.
                 ************************************************************/
                uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
-                               prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data));
+                               ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
 
                if (!uptodate)
                {
@@ -1485,7 +1483,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
                MemSet(prodesc, 0, sizeof(plperl_proc_desc));
                prodesc->proname = strdup(internal_proname);
                prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
-               prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
+               prodesc->fn_tid = procTup->t_self;
 
                /* Remember if function is STABLE/IMMUTABLE */
                prodesc->fn_readonly =
@@ -2128,9 +2126,9 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
        PG_TRY();
        {
                /************************************************************
-                * Resolve argument type names and then look them up by oid 
-         * in the system cache, and remember the required information 
-         * for input conversion.
+                * Resolve argument type names and then look them up by oid
+                * in the system cache, and remember the required information
+                * for input conversion.
                 ************************************************************/
                for (i = 0; i < argc; i++)
                {
@@ -2523,8 +2521,8 @@ plperl_spi_freeplan(char *query)
         * free all memory before SPI_freeplan, so if it dies, nothing will be
         * left over
         */
-       hash_search(plperl_query_hash, query, 
-                               HASH_REMOVE,NULL);
+       hash_search(plperl_query_hash, query,
+                               HASH_REMOVE, NULL);
 
        plan = qdesc->plan;
        free(qdesc->argtypes);
index c342ed3e85b01e8e402b2f12b1c867e4a3dd6c96..44fae0f1b2831f2358d2d7d042038bde6d5bb0fb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.112 2007/02/08 18:37:14 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.113 2007/02/09 03:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -165,7 +165,7 @@ recheck:
        {
                /* We have a compiled function, but is it still valid? */
                if (function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
-                       function->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data))
+                       ItemPointerEquals(&function->fn_tid, &procTup->t_self))
                        function_valid = true;
                else
                {
@@ -355,7 +355,7 @@ do_compile(FunctionCallInfo fcinfo,
        function->fn_name = pstrdup(NameStr(procStruct->proname));
        function->fn_oid = fcinfo->flinfo->fn_oid;
        function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
-       function->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
+       function->fn_tid = procTup->t_self;
        function->fn_functype = functype;
        function->fn_cxt = func_cxt;
        function->out_param_varno = -1;         /* set up for no OUT param */
index dad5ba5bb4b3fae9e582937ca9f5f727b8cbbc7d..9f29fcd5da51721785e17f571e7433ddb031b82a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.84 2007/01/30 22:05:13 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.85 2007/02/09 03:35:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -547,7 +547,7 @@ typedef struct PLpgSQL_function
        char       *fn_name;
        Oid                     fn_oid;
        TransactionId fn_xmin;
-       CommandId       fn_cmin;
+       ItemPointerData fn_tid;
        int                     fn_functype;
        PLpgSQL_func_hashkey *fn_hashkey;       /* back-link to hashtable key */
        MemoryContext fn_cxt;
index cd0f701581eb479c19d6d2f0a020daf4a0eb9d20..90a3f87b150c884df27a63238a0e711aa4ae4ccb 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.94 2007/02/01 19:10:30 momjian Exp $
+ *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.95 2007/02/09 03:35:35 tgl Exp $
  *
  *********************************************************************
  */
@@ -123,7 +123,7 @@ typedef struct PLyProcedure
        char       *proname;            /* SQL name of procedure */
        char       *pyname;                     /* Python name of procedure */
        TransactionId fn_xmin;
-       CommandId       fn_cmin;
+       ItemPointerData fn_tid;
        bool            fn_readonly;
        PLyTypeInfo result;                     /* also used to store info for trigger tuple
                                                                 * type */
@@ -1100,7 +1100,7 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
                        elog(FATAL, "proc->me != plproc");
                /* did we find an up-to-date cache entry? */
                if (proc->fn_xmin != HeapTupleHeaderGetXmin(procTup->t_data) ||
-                       proc->fn_cmin != HeapTupleHeaderGetCmin(procTup->t_data))
+                       !ItemPointerEquals(&proc->fn_tid, &procTup->t_self))
                {
                        Py_DECREF(plproc);
                        proc = NULL;
@@ -1151,7 +1151,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
        proc->proname = PLy_strdup(NameStr(procStruct->proname));
        proc->pyname = PLy_strdup(procName);
        proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
-       proc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
+       proc->fn_tid = procTup->t_self;
        /* Remember if function is STABLE/IMMUTABLE */
        proc->fn_readonly =
                (procStruct->provolatile != PROVOLATILE_VOLATILE);
index 7611fa7cc76ca8460059bfc0193c510382abb730..0477fa8c56c0e79c0e6d80d4cbe3f487e7c65e7f 100644 (file)
@@ -2,7 +2,7 @@
  * pltcl.c             - PostgreSQL support for Tcl as
  *                               procedural language (PL)
  *
- *       $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.109 2007/02/01 19:10:30 momjian Exp $
+ *       $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.110 2007/02/09 03:35:35 tgl Exp $
  *
  **********************************************************************/
 
@@ -76,7 +76,7 @@ typedef struct pltcl_proc_desc
 {
        char       *proname;
        TransactionId fn_xmin;
-       CommandId       fn_cmin;
+       ItemPointerData fn_tid;
        bool            fn_readonly;
        bool            lanpltrusted;
        FmgrInfo        result_in_func;
@@ -962,7 +962,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
                prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
 
                uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
-                               prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data));
+                               ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
 
                if (!uptodate)
                {
@@ -1004,7 +1004,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
                MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
                prodesc->proname = strdup(internal_proname);
                prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
-               prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
+               prodesc->fn_tid = procTup->t_self;
 
                /* Remember if function is STABLE/IMMUTABLE */
                prodesc->fn_readonly =
diff --git a/src/test/regress/expected/combocid.out b/src/test/regress/expected/combocid.out
new file mode 100644 (file)
index 0000000..14e45fe
--- /dev/null
@@ -0,0 +1,242 @@
+--
+-- Tests for some likely failure cases with combo cmin/cmax mechanism
+--
+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 VALUES (1);
+INSERT INTO combocidtest VALUES (2);
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |   10 |      1
+ (0,2) |   11 |      2
+(2 rows)
+
+SAVEPOINT s1;
+UPDATE combocidtest SET foobar = foobar + 10;
+-- here we should see only updated tuples
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,3) |   13 |     11
+ (0,4) |   13 |     12
+(2 rows)
+
+ROLLBACK TO s1;
+-- now we should see old tuples, but with combo CIDs starting at 0
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |    0 |      1
+ (0,2) |    1 |      2
+(2 rows)
+
+COMMIT;
+-- combo data is not there anymore, but should still see tuples
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |    0 |      1
+ (0,2) |    1 |      2
+(2 rows)
+
+-- Test combo cids with portals
+BEGIN;
+INSERT INTO combocidtest VALUES (333);
+DECLARE c CURSOR FOR SELECT ctid,cmin,* FROM combocidtest;
+DELETE FROM combocidtest;
+FETCH ALL FROM c;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |    2 |      1
+ (0,2) |    2 |      2
+ (0,5) |    0 |    333
+(3 rows)
+
+ROLLBACK;
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |    2 |      1
+ (0,2) |    2 |      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 VALUES (444);
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |    2 |      1
+ (0,2) |    2 |      2
+ (0,6) |   10 |    444
+(3 rows)
+
+SAVEPOINT s1;
+-- this doesn't affect cmin
+SELECT ctid,cmin,* FROM combocidtest FOR UPDATE;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |    2 |      1
+ (0,2) |    2 |      2
+ (0,6) |   10 |    444
+(3 rows)
+
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |    2 |      1
+ (0,2) |    2 |      2
+ (0,6) |   10 |    444
+(3 rows)
+
+-- but this does
+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
+(3 rows)
+
+ROLLBACK TO s1;
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |   14 |      1
+ (0,2) |   14 |      2
+ (0,6) |    0 |    444
+(3 rows)
+
+COMMIT;
+SELECT ctid,cmin,* FROM combocidtest;
+ ctid  | cmin | foobar 
+-------+------+--------
+ (0,1) |   14 |      1
+ (0,2) |   14 |      2
+ (0,6) |    0 |    444
+(3 rows)
+
index c32daf815d6251e3eed4bd5098c81778d0be1fb9..cb2c0c0137197f68a632c83caddfa6c99a111a6c 100644 (file)
@@ -5,14 +5,15 @@
 -- This test tries to verify that WITHOUT OIDS actually saves space.
 -- On machines where MAXALIGN is 8, WITHOUT OIDS may or may not save any
 -- space, depending on the size of the tuple header + null bitmap.
--- As of 8.0 we need a 9-bit null bitmap to force the difference to appear.
+-- As of 8.3 we need a null bitmap of 8 or less bits for the difference
+-- to appear.
 --
 CREATE TABLE wi (i INT,
                  n1 int, n2 int, n3 int, n4 int,
-                 n5 int, n6 int, n7 int, n8 int) WITH OIDS;
+                 n5 int, n6 int, n7 int) WITH OIDS;
 CREATE TABLE wo (i INT,
                  n1 int, n2 int, n3 int, n4 int,
-                 n5 int, n6 int, n7 int, n8 int) WITHOUT OIDS;
+                 n5 int, n6 int, n7 int) WITHOUT OIDS;
 INSERT INTO wi VALUES (1);  -- 1
 INSERT INTO wo SELECT i FROM wi;  -- 1
 INSERT INTO wo SELECT i+1 FROM wi;  -- 1+1=2
index efc1f1fef6e2cfa7fba2a3ec36bff1f5e30f2d87..096d2c1c7a777335431eb4d78ff599efe7f69926 100644 (file)
@@ -1,6 +1,6 @@
 # ----------
 # The first group of parallel test
-# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.38 2007/01/28 16:16:54 neilc Exp $
+# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.39 2007/02/09 03:35:35 tgl Exp $
 # ----------
 test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid
 
@@ -69,7 +69,7 @@ test: misc
 # ----------
 # The fifth group of parallel test
 # ----------
-test: select_views portals_p2 rules foreign_key cluster dependency guc
+test: select_views portals_p2 rules foreign_key cluster dependency guc combocid
 
 # ----------
 # The sixth group of parallel test
index 75e8b83138b33e7003964d188573c17debd942bd..d109dabdc25fbbd53d50c89707b4838b2c7823f5 100644 (file)
@@ -1,4 +1,4 @@
-# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.36 2007/01/28 16:16:54 neilc Exp $
+# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.37 2007/02/09 03:35:35 tgl Exp $
 # This should probably be in an order similar to parallel_schedule.
 test: boolean
 test: char
@@ -88,6 +88,7 @@ test: foreign_key
 test: cluster
 test: dependency
 test: guc
+test: combocid
 test: limit
 test: plpgsql
 test: copy2
diff --git a/src/test/regress/sql/combocid.sql b/src/test/regress/sql/combocid.sql
new file mode 100644 (file)
index 0000000..3f30839
--- /dev/null
@@ -0,0 +1,93 @@
+--
+-- Tests for some likely failure cases with combo cmin/cmax mechanism
+--
+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 VALUES (1);
+INSERT INTO combocidtest VALUES (2);
+
+SELECT ctid,cmin,* FROM combocidtest;
+
+SAVEPOINT s1;
+
+UPDATE combocidtest SET foobar = foobar + 10;
+
+-- here we should see only updated tuples
+SELECT ctid,cmin,* FROM combocidtest;
+
+ROLLBACK TO s1;
+
+-- now we should see old tuples, but with combo CIDs starting at 0
+SELECT ctid,cmin,* FROM combocidtest;
+
+COMMIT;
+
+-- combo data is not there anymore, but should still see tuples
+SELECT ctid,cmin,* FROM combocidtest;
+
+-- Test combo cids with portals
+BEGIN;
+
+INSERT INTO combocidtest VALUES (333);
+
+DECLARE c CURSOR FOR SELECT ctid,cmin,* FROM combocidtest;
+
+DELETE FROM combocidtest;
+
+FETCH ALL FROM c;
+
+ROLLBACK;
+
+SELECT ctid,cmin,* FROM combocidtest;
+
+-- check behavior with locked tuples
+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 VALUES (444);
+
+SELECT ctid,cmin,* FROM combocidtest;
+
+SAVEPOINT s1;
+
+-- this doesn't affect cmin
+SELECT ctid,cmin,* FROM combocidtest FOR UPDATE;
+SELECT ctid,cmin,* FROM combocidtest;
+
+-- but this does
+UPDATE combocidtest SET foobar = foobar + 10;
+
+SELECT ctid,cmin,* FROM combocidtest;
+
+ROLLBACK TO s1;
+
+SELECT ctid,cmin,* FROM combocidtest;
+
+COMMIT;
+
+SELECT ctid,cmin,* FROM combocidtest;
index 1a10a8533dff3d0f502b43f5f857e46052192de8..9fbb454d4dc22b87ac0f6980811278819ef4d24f 100644 (file)
@@ -6,14 +6,15 @@
 -- This test tries to verify that WITHOUT OIDS actually saves space.
 -- On machines where MAXALIGN is 8, WITHOUT OIDS may or may not save any
 -- space, depending on the size of the tuple header + null bitmap.
--- As of 8.0 we need a 9-bit null bitmap to force the difference to appear.
+-- As of 8.3 we need a null bitmap of 8 or less bits for the difference
+-- to appear.
 --
 CREATE TABLE wi (i INT,
                  n1 int, n2 int, n3 int, n4 int,
-                 n5 int, n6 int, n7 int, n8 int) WITH OIDS;
+                 n5 int, n6 int, n7 int) WITH OIDS;
 CREATE TABLE wo (i INT,
                  n1 int, n2 int, n3 int, n4 int,
-                 n5 int, n6 int, n7 int, n8 int) WITHOUT OIDS;
+                 n5 int, n6 int, n7 int) WITHOUT OIDS;
 
 INSERT INTO wi VALUES (1);  -- 1
 INSERT INTO wo SELECT i FROM wi;  -- 1