Commit c3983003 authored by Tom Lane's avatar Tom Lane

Combine cmin and cmax fields of HeapTupleHeaders into a single field, by

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.
parent acb34166
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -582,14 +582,18 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
case MinTransactionIdAttributeNumber: case MinTransactionIdAttributeNumber:
result = TransactionIdGetDatum(HeapTupleHeaderGetXmin(tup->t_data)); result = TransactionIdGetDatum(HeapTupleHeaderGetXmin(tup->t_data));
break; break;
case MinCommandIdAttributeNumber:
result = CommandIdGetDatum(HeapTupleHeaderGetCmin(tup->t_data));
break;
case MaxTransactionIdAttributeNumber: case MaxTransactionIdAttributeNumber:
result = TransactionIdGetDatum(HeapTupleHeaderGetXmax(tup->t_data)); result = TransactionIdGetDatum(HeapTupleHeaderGetXmax(tup->t_data));
break; break;
case MinCommandIdAttributeNumber:
case MaxCommandIdAttributeNumber: 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; break;
case TableOidAttributeNumber: case TableOidAttributeNumber:
result = ObjectIdGetDatum(tup->t_tableOid); result = ObjectIdGetDatum(tup->t_tableOid);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * INTERFACE ROUTINES
...@@ -1407,8 +1407,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, ...@@ -1407,8 +1407,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
tup->t_data->t_infomask |= HEAP_XMAX_INVALID; tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
HeapTupleHeaderSetXmin(tup->t_data, xid); HeapTupleHeaderSetXmin(tup->t_data, xid);
HeapTupleHeaderSetCmin(tup->t_data, cid); HeapTupleHeaderSetCmin(tup->t_data, cid);
HeapTupleHeaderSetXmax(tup->t_data, 0); /* zero out Datum fields */ HeapTupleHeaderSetXmax(tup->t_data, 0); /* for cleanliness */
HeapTupleHeaderSetCmax(tup->t_data, 0); /* for cleanliness */
tup->t_tableOid = RelationGetRelid(relation); tup->t_tableOid = RelationGetRelid(relation);
/* /*
...@@ -1585,6 +1584,7 @@ heap_delete(Relation relation, ItemPointer tid, ...@@ -1585,6 +1584,7 @@ heap_delete(Relation relation, ItemPointer tid,
PageHeader dp; PageHeader dp;
Buffer buffer; Buffer buffer;
bool have_tuple_lock = false; bool have_tuple_lock = false;
bool iscombo;
Assert(ItemPointerIsValid(tid)); Assert(ItemPointerIsValid(tid));
...@@ -1724,6 +1724,9 @@ l1: ...@@ -1724,6 +1724,9 @@ l1:
return result; return result;
} }
/* replace cid with a combo cid if necessary */
HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
START_CRIT_SECTION(); START_CRIT_SECTION();
/* store transaction information of xact deleting the tuple */ /* store transaction information of xact deleting the tuple */
...@@ -1733,7 +1736,7 @@ l1: ...@@ -1733,7 +1736,7 @@ l1:
HEAP_IS_LOCKED | HEAP_IS_LOCKED |
HEAP_MOVED); HEAP_MOVED);
HeapTupleHeaderSetXmax(tp.t_data, xid); 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 */ /* Make sure there is no forward chain link in t_ctid */
tp.t_data->t_ctid = tp.t_self; tp.t_data->t_ctid = tp.t_self;
...@@ -1893,6 +1896,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, ...@@ -1893,6 +1896,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
Size newtupsize, Size newtupsize,
pagefree; pagefree;
bool have_tuple_lock = false; bool have_tuple_lock = false;
bool iscombo;
Assert(ItemPointerIsValid(otid)); Assert(ItemPointerIsValid(otid));
...@@ -2058,8 +2062,13 @@ l2: ...@@ -2058,8 +2062,13 @@ l2:
newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
HeapTupleHeaderSetXmin(newtup->t_data, xid); HeapTupleHeaderSetXmin(newtup->t_data, xid);
HeapTupleHeaderSetCmin(newtup->t_data, cid); HeapTupleHeaderSetCmin(newtup->t_data, cid);
HeapTupleHeaderSetXmax(newtup->t_data, 0); /* zero out Datum fields */ HeapTupleHeaderSetXmax(newtup->t_data, 0); /* for cleanliness */
HeapTupleHeaderSetCmax(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 * If the toaster needs to be activated, OR if the new tuple will not fit
...@@ -2088,7 +2097,7 @@ l2: ...@@ -2088,7 +2097,7 @@ l2:
HEAP_IS_LOCKED | HEAP_IS_LOCKED |
HEAP_MOVED); HEAP_MOVED);
HeapTupleHeaderSetXmax(oldtup.t_data, xid); HeapTupleHeaderSetXmax(oldtup.t_data, xid);
HeapTupleHeaderSetCmax(oldtup.t_data, cid); HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
/* temporarily make it look not-updated */ /* temporarily make it look not-updated */
oldtup.t_data->t_ctid = oldtup.t_self; oldtup.t_data->t_ctid = oldtup.t_self;
already_marked = true; already_marked = true;
...@@ -2183,7 +2192,7 @@ l2: ...@@ -2183,7 +2192,7 @@ l2:
HEAP_IS_LOCKED | HEAP_IS_LOCKED |
HEAP_MOVED); HEAP_MOVED);
HeapTupleHeaderSetXmax(oldtup.t_data, xid); 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 */ /* record address of new tuple in t_ctid of old one */
...@@ -2687,12 +2696,11 @@ l3: ...@@ -2687,12 +2696,11 @@ l3:
/* /*
* Store transaction information of xact locking the tuple. * Store transaction information of xact locking the tuple.
* *
* Note: our CID is meaningless if storing a MultiXactId, but no harm in * Note: Cmax is meaningless in this context, so don't set it; this
* storing it anyway. * avoids possibly generating a useless combo CID.
*/ */
tuple->t_data->t_infomask = new_infomask; tuple->t_data->t_infomask = new_infomask;
HeapTupleHeaderSetXmax(tuple->t_data, xid); HeapTupleHeaderSetXmax(tuple->t_data, xid);
HeapTupleHeaderSetCmax(tuple->t_data, cid);
/* Make sure there is no forward chain link in t_ctid */ /* Make sure there is no forward chain link in t_ctid */
tuple->t_data->t_ctid = *tid; tuple->t_data->t_ctid = *tid;
...@@ -3443,7 +3451,7 @@ heap_xlog_delete(XLogRecPtr lsn, XLogRecord *record) ...@@ -3443,7 +3451,7 @@ heap_xlog_delete(XLogRecPtr lsn, XLogRecord *record)
HEAP_IS_LOCKED | HEAP_IS_LOCKED |
HEAP_MOVED); HEAP_MOVED);
HeapTupleHeaderSetXmax(htup, record->xl_xid); HeapTupleHeaderSetXmax(htup, record->xl_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId); HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
/* Make sure there is no forward chain link in t_ctid */ /* Make sure there is no forward chain link in t_ctid */
htup->t_ctid = xlrec->target.tid; htup->t_ctid = xlrec->target.tid;
PageSetLSN(page, lsn); PageSetLSN(page, lsn);
...@@ -3608,7 +3616,7 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool move) ...@@ -3608,7 +3616,7 @@ heap_xlog_update(XLogRecPtr lsn, XLogRecord *record, bool move)
HEAP_IS_LOCKED | HEAP_IS_LOCKED |
HEAP_MOVED); HEAP_MOVED);
HeapTupleHeaderSetXmax(htup, record->xl_xid); HeapTupleHeaderSetXmax(htup, record->xl_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId); HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
/* Set forward chain link in t_ctid */ /* Set forward chain link in t_ctid */
htup->t_ctid = xlrec->newtid; htup->t_ctid = xlrec->newtid;
} }
...@@ -3761,7 +3769,7 @@ heap_xlog_lock(XLogRecPtr lsn, XLogRecord *record) ...@@ -3761,7 +3769,7 @@ heap_xlog_lock(XLogRecPtr lsn, XLogRecord *record)
else else
htup->t_infomask |= HEAP_XMAX_EXCL_LOCK; htup->t_infomask |= HEAP_XMAX_EXCL_LOCK;
HeapTupleHeaderSetXmax(htup, xlrec->locking_xid); HeapTupleHeaderSetXmax(htup, xlrec->locking_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId); HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
/* Make sure there is no forward chain link in t_ctid */ /* Make sure there is no forward chain link in t_ctid */
htup->t_ctid = xlrec->target.tid; htup->t_ctid = xlrec->target.tid;
PageSetLSN(page, lsn); PageSetLSN(page, lsn);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,11 +38,12 @@ ...@@ -38,11 +38,12 @@
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/combocid.h"
#include "utils/flatfiles.h" #include "utils/flatfiles.h"
#include "utils/guc.h"
#include "utils/inval.h" #include "utils/inval.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/relcache.h" #include "utils/relcache.h"
#include "utils/guc.h"
/* /*
...@@ -1628,6 +1629,7 @@ CommitTransaction(void) ...@@ -1628,6 +1629,7 @@ CommitTransaction(void)
AtEOXact_Namespace(true); AtEOXact_Namespace(true);
/* smgrcommit already done */ /* smgrcommit already done */
AtEOXact_Files(); AtEOXact_Files();
AtEOXact_ComboCid();
pgstat_clear_snapshot(); pgstat_clear_snapshot();
pgstat_count_xact_commit(); pgstat_count_xact_commit();
pgstat_report_txn_timestamp(0); pgstat_report_txn_timestamp(0);
...@@ -1845,6 +1847,7 @@ PrepareTransaction(void) ...@@ -1845,6 +1847,7 @@ PrepareTransaction(void)
AtEOXact_Namespace(true); AtEOXact_Namespace(true);
/* smgrcommit already done */ /* smgrcommit already done */
AtEOXact_Files(); AtEOXact_Files();
AtEOXact_ComboCid();
pgstat_clear_snapshot(); pgstat_clear_snapshot();
CurrentResourceOwner = NULL; CurrentResourceOwner = NULL;
...@@ -1997,6 +2000,7 @@ AbortTransaction(void) ...@@ -1997,6 +2000,7 @@ AbortTransaction(void)
AtEOXact_Namespace(false); AtEOXact_Namespace(false);
smgrabort(); smgrabort();
AtEOXact_Files(); AtEOXact_Files();
AtEOXact_ComboCid();
pgstat_clear_snapshot(); pgstat_clear_snapshot();
pgstat_count_xact_rollback(); pgstat_count_xact_rollback();
pgstat_report_txn_timestamp(0); pgstat_report_txn_timestamp(0);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 ...@@ -64,7 +64,7 @@ typedef struct
/* fn_oid is the hash key and so must be first! */ /* fn_oid is the hash key and so must be first! */
Oid fn_oid; /* OID of an external C function */ Oid fn_oid; /* OID of an external C function */
TransactionId fn_xmin; /* for checking up-to-dateness */ TransactionId fn_xmin; /* for checking up-to-dateness */
CommandId fn_cmin; ItemPointerData fn_tid;
PGFunction user_fn; /* the function's address */ PGFunction user_fn; /* the function's address */
const Pg_finfo_record *inforec; /* address of its info record */ const Pg_finfo_record *inforec; /* address of its info record */
} CFuncHashTabEntry; } CFuncHashTabEntry;
...@@ -483,7 +483,7 @@ lookup_C_func(HeapTuple procedureTuple) ...@@ -483,7 +483,7 @@ lookup_C_func(HeapTuple procedureTuple)
if (entry == NULL) if (entry == NULL)
return NULL; /* no such entry */ return NULL; /* no such entry */
if (entry->fn_xmin == HeapTupleHeaderGetXmin(procedureTuple->t_data) && 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 entry; /* OK */
return NULL; /* entry is out of date */ return NULL; /* entry is out of date */
} }
...@@ -521,7 +521,7 @@ record_C_func(HeapTuple procedureTuple, ...@@ -521,7 +521,7 @@ record_C_func(HeapTuple procedureTuple,
&found); &found);
/* OID is already filled in */ /* OID is already filled in */
entry->fn_xmin = HeapTupleHeaderGetXmin(procedureTuple->t_data); 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->user_fn = user_fn;
entry->inforec = inforec; entry->inforec = inforec;
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for utils/time # Makefile for utils/time
# #
# IDENTIFICATION # 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 ...@@ -12,12 +12,12 @@ subdir = src/backend/utils/time
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
OBJS = tqual.o OBJS = combocid.o tqual.o
all: SUBSYS.o all: SUBSYS.o
SUBSYS.o: $(OBJS) SUBSYS.o: $(OBJS)
$(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS) $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS)
clean: clean:
rm -f SUBSYS.o $(OBJS) rm -f SUBSYS.o $(OBJS)
/*-------------------------------------------------------------------------
*
* 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;
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -45,7 +45,7 @@
/* /*
* Heap tuple header. To avoid wasting space, the fields should be * 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 * 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 * as on-disk tuples, so that the same routines can be used to build and
...@@ -65,17 +65,18 @@ ...@@ -65,17 +65,18 @@
* object ID (if HEAP_HASOID is set in t_infomask) * object ID (if HEAP_HASOID is set in t_infomask)
* user data fields * user data fields
* *
* We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac in four * We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac in three
* physical fields. Xmin, Cmin and Xmax are always really stored, but * physical fields. Xmin and Xmax are always really stored, but Cmin, Cmax
* Cmax and Xvac share a field. This works because we know that there are * and Xvac share a field. This works because we know that Cmin and Cmax
* only a limited number of states that a tuple can be in, and that Cmax * are only interesting for the lifetime of the inserting and deleting
* is only interesting for the lifetime of the deleting transaction. * transaction respectively. If a tuple is inserted and deleted in the same
* This assumes that VACUUM FULL never tries to move a tuple whose Cmax * transaction, we store a "combo" command id that can be mapped to the real
* is still interesting (ie, delete-in-progress). * 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
* Note that in 7.3 and 7.4 a similar idea was applied to Xmax and Cmin. * by VACUUM FULL, which does not have any command sub-structure and so does
* However, with the advent of subtransactions, a tuple may need both Xmax * not need either Cmin or Cmax. (This requires that VACUUM FULL never try
* and Cmin simultaneously, so this is no longer possible. * 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 * 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, * is initialized with its own TID (location). If the tuple is ever updated,
...@@ -103,14 +104,13 @@ ...@@ -103,14 +104,13 @@
typedef struct HeapTupleFields typedef struct HeapTupleFields
{ {
TransactionId t_xmin; /* inserting xact ID */ TransactionId t_xmin; /* inserting xact ID */
CommandId t_cmin; /* inserting command ID */
TransactionId t_xmax; /* deleting or locking xact ID */ TransactionId t_xmax; /* deleting or locking xact ID */
union 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 */ TransactionId t_xvac; /* VACUUM FULL xact ID */
} t_field4; } t_field3;
} HeapTupleFields; } HeapTupleFields;
typedef struct DatumTupleFields typedef struct DatumTupleFields
...@@ -145,7 +145,7 @@ typedef struct HeapTupleHeaderData ...@@ -145,7 +145,7 @@ typedef struct HeapTupleHeaderData
uint8 t_hoff; /* sizeof header incl. bitmap, padding */ uint8 t_hoff; /* sizeof header incl. bitmap, padding */
/* ^ - 27 bytes - ^ */ /* ^ - 23 bytes - ^ */
bits8 t_bits[1]; /* bitmap of NULLs -- VARIABLE LENGTH */ bits8 t_bits[1]; /* bitmap of NULLs -- VARIABLE LENGTH */
...@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader; ...@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
#define HEAP_HASCOMPRESSED 0x0008 /* has compressed stored attribute(s) */ #define HEAP_HASCOMPRESSED 0x0008 /* has compressed stored attribute(s) */
#define HEAP_HASEXTENDED 0x000C /* the two above combined */ #define HEAP_HASEXTENDED 0x000C /* the two above combined */
#define HEAP_HASOID 0x0010 /* has an object-id field */ #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_EXCL_LOCK 0x0040 /* xmax is exclusive locker */
#define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared 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 */ /* if either LOCK bit is set, xmax hasn't deleted the tuple, only locked it */
...@@ -180,17 +180,13 @@ typedef HeapTupleHeaderData *HeapTupleHeader; ...@@ -180,17 +180,13 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
* FULL */ * FULL */
#define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN) #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 */ #define HEAP_NATTS_MASK 0x7FF /* 11 bits for number of attributes */
/* bits 0xF800 are unused */ /* bits 0xF800 are currently unused */
#define HeapTupleHeaderGetNatts(tup) ((tup)->t_infomask2 & HEAP_NATTS_MASK)
#define HeapTupleHeaderSetNatts(tup, natts) \
( \
(tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \
)
/* /*
* HeapTupleHeader accessor macros * HeapTupleHeader accessor macros
...@@ -219,39 +215,40 @@ typedef HeapTupleHeaderData *HeapTupleHeader; ...@@ -219,39 +215,40 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
TransactionIdStore((xid), &(tup)->t_choice.t_heap.t_xmax) \ 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 * HeapTupleHeaderGetRawCommandId will give you what's in the header whether
* by a transaction other than the inserting one. We could check * it is useful or not. Most code should use HeapTupleHeaderGetCmin or
* HEAP_XMAX_INVALID and return FirstCommandId if it's clear, but since that * HeapTupleHeaderGetCmax instead, but note that those Assert that you can
* bit will be set again if the deleting transaction aborts, there'd be no * get a legitimate result, ie you are in the originating transaction!
* real gain in safety from the extra test. So, just rely on the caller not
* to trust the value unless it's meaningful.
*/ */
#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 { \ do { \
Assert(!((tup)->t_infomask & HEAP_MOVED)); \ 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) } while (0)
#define HeapTupleHeaderGetXvac(tup) \ #define HeapTupleHeaderGetXvac(tup) \
( \ ( \
((tup)->t_infomask & HEAP_MOVED) ? \ ((tup)->t_infomask & HEAP_MOVED) ? \
(tup)->t_choice.t_heap.t_field4.t_xvac \ (tup)->t_choice.t_heap.t_field3.t_xvac \
: \ : \
InvalidTransactionId \ InvalidTransactionId \
) )
...@@ -259,7 +256,7 @@ do { \ ...@@ -259,7 +256,7 @@ do { \
#define HeapTupleHeaderSetXvac(tup, xid) \ #define HeapTupleHeaderSetXvac(tup, xid) \
do { \ do { \
Assert((tup)->t_infomask & HEAP_MOVED); \ 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) } while (0)
#define HeapTupleHeaderGetDatumLength(tup) \ #define HeapTupleHeaderGetDatumLength(tup) \
...@@ -306,6 +303,14 @@ do { \ ...@@ -306,6 +303,14 @@ do { \
*((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid); \ *((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid); \
} while (0) } 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) - * BITMAPLEN(NATTS) -
...@@ -374,9 +379,9 @@ do { \ ...@@ -374,9 +379,9 @@ do { \
* and thereby prevent accidental use of the nonexistent fields. * and thereby prevent accidental use of the nonexistent fields.
* *
* MinimalTupleData contains a length word, some padding, and fields matching * MinimalTupleData contains a length word, some padding, and fields matching
* HeapTupleHeaderData beginning with t_infomask2. The padding is chosen so that * HeapTupleHeaderData beginning with t_infomask2. The padding is chosen so
* offsetof(t_infomask2) is the same modulo MAXIMUM_ALIGNOF in both structs. * that offsetof(t_infomask2) is the same modulo MAXIMUM_ALIGNOF in both
* This makes data alignment rules equivalent in both cases. * structs. This makes data alignment rules equivalent in both cases.
* *
* When a minimal tuple is accessed via a HeapTupleData pointer, t_data is * 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 * set to point MINIMAL_TUPLE_OFFSET bytes before the actual start of the
...@@ -405,7 +410,7 @@ typedef struct MinimalTupleData ...@@ -405,7 +410,7 @@ typedef struct MinimalTupleData
uint8 t_hoff; /* sizeof header incl. bitmap, padding */ uint8 t_hoff; /* sizeof header incl. bitmap, padding */
/* ^ - 27 bytes - ^ */ /* ^ - 23 bytes - ^ */
bits8 t_bits[1]; /* bitmap of NULLs -- VARIABLE LENGTH */ bits8 t_bits[1]; /* bitmap of NULLs -- VARIABLE LENGTH */
...@@ -638,4 +643,11 @@ typedef struct xl_heap_freeze ...@@ -638,4 +643,11 @@ typedef struct xl_heap_freeze
#define SizeOfHeapFreeze (offsetof(xl_heap_freeze, cutoff_xid) + sizeof(TransactionId)) #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 */ #endif /* HTUP_H */
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200702071 #define CATALOG_VERSION_NO 200702081
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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; ...@@ -132,10 +132,11 @@ typedef PageHeaderData *PageHeader;
/* /*
* Page layout version number 0 is for pre-7.3 Postgres releases. * 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. * Releases 7.3 and 7.4 use 1, denoting a new HeapTupleHeader layout.
* Release 8.0 changed the HeapTupleHeader layout again. * Release 8.0 uses 2; it changed the HeapTupleHeader layout again.
* Release 8.1 redefined HeapTupleHeader infomask bits. * 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
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
......
/*-------------------------------------------------------------------------
*
* 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 */
/********************************************************************** /**********************************************************************
* plperl.c - perl as a procedural language for PostgreSQL * 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 ...@@ -41,7 +41,7 @@ typedef struct plperl_proc_desc
{ {
char *proname; char *proname;
TransactionId fn_xmin; TransactionId fn_xmin;
CommandId fn_cmin; ItemPointerData fn_tid;
bool fn_readonly; bool fn_readonly;
bool lanpltrusted; bool lanpltrusted;
bool fn_retistuple; /* true, if function returns tuple */ bool fn_retistuple; /* true, if function returns tuple */
...@@ -296,7 +296,7 @@ _PG_init(void) ...@@ -296,7 +296,7 @@ _PG_init(void)
* *
* We start out by creating a "held" interpreter that we can use in * 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 * 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 * 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. * 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 * We detect if it is safe to run two interpreters during the setup of the
...@@ -304,7 +304,7 @@ _PG_init(void) ...@@ -304,7 +304,7 @@ _PG_init(void)
*/ */
static void static void
check_interp(bool trusted) check_interp(bool trusted)
{ {
if (interp_state == INTERP_HELD) if (interp_state == INTERP_HELD)
...@@ -322,7 +322,7 @@ check_interp(bool trusted) ...@@ -322,7 +322,7 @@ check_interp(bool trusted)
plperl_held_interp = NULL; plperl_held_interp = NULL;
trusted_context = trusted; trusted_context = trusted;
} }
else if (interp_state == INTERP_BOTH || else if (interp_state == INTERP_BOTH ||
(trusted && interp_state == INTERP_TRUSTED) || (trusted && interp_state == INTERP_TRUSTED) ||
(!trusted && interp_state == INTERP_UNTRUSTED)) (!trusted && interp_state == INTERP_UNTRUSTED))
{ {
...@@ -349,11 +349,9 @@ check_interp(bool trusted) ...@@ -349,11 +349,9 @@ check_interp(bool trusted)
} }
else else
{ {
elog(ERROR, elog(ERROR,
"cannot allocate second Perl interpreter on this platform"); "cannot allocate second Perl interpreter on this platform");
} }
} }
...@@ -425,7 +423,7 @@ plperl_init_interp(void) ...@@ -425,7 +423,7 @@ plperl_init_interp(void)
elog(ERROR, "could not allocate Perl interpreter"); elog(ERROR, "could not allocate Perl interpreter");
perl_construct(plperl_held_interp); 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); 3, embedding, NULL);
perl_run(plperl_held_interp); perl_run(plperl_held_interp);
...@@ -434,7 +432,7 @@ plperl_init_interp(void) ...@@ -434,7 +432,7 @@ plperl_init_interp(void)
SV *res; SV *res;
res = eval_pv(TEST_FOR_MULTI,TRUE); res = eval_pv(TEST_FOR_MULTI,TRUE);
can_run_two = SvIV(res); can_run_two = SvIV(res);
interp_state = INTERP_HELD; interp_state = INTERP_HELD;
} }
...@@ -1430,7 +1428,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) ...@@ -1430,7 +1428,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
/************************************************************ /************************************************************
* Lookup the internal proc name in the hashtable * 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); HASH_FIND, NULL);
if (hash_entry) if (hash_entry)
...@@ -1445,7 +1443,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) ...@@ -1445,7 +1443,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
* function's pg_proc entry without changing its OID. * function's pg_proc entry without changing its OID.
************************************************************/ ************************************************************/
uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) && uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)); ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
if (!uptodate) if (!uptodate)
{ {
...@@ -1485,7 +1483,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) ...@@ -1485,7 +1483,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
MemSet(prodesc, 0, sizeof(plperl_proc_desc)); MemSet(prodesc, 0, sizeof(plperl_proc_desc));
prodesc->proname = strdup(internal_proname); prodesc->proname = strdup(internal_proname);
prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); 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 */ /* Remember if function is STABLE/IMMUTABLE */
prodesc->fn_readonly = prodesc->fn_readonly =
...@@ -2128,9 +2126,9 @@ plperl_spi_prepare(char *query, int argc, SV **argv) ...@@ -2128,9 +2126,9 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
PG_TRY(); PG_TRY();
{ {
/************************************************************ /************************************************************
* Resolve argument type names and then look them up by oid * Resolve argument type names and then look them up by oid
* in the system cache, and remember the required information * in the system cache, and remember the required information
* for input conversion. * for input conversion.
************************************************************/ ************************************************************/
for (i = 0; i < argc; i++) for (i = 0; i < argc; i++)
{ {
...@@ -2523,8 +2521,8 @@ plperl_spi_freeplan(char *query) ...@@ -2523,8 +2521,8 @@ plperl_spi_freeplan(char *query)
* free all memory before SPI_freeplan, so if it dies, nothing will be * free all memory before SPI_freeplan, so if it dies, nothing will be
* left over * left over
*/ */
hash_search(plperl_query_hash, query, hash_search(plperl_query_hash, query,
HASH_REMOVE,NULL); HASH_REMOVE, NULL);
plan = qdesc->plan; plan = qdesc->plan;
free(qdesc->argtypes); free(qdesc->argtypes);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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: ...@@ -165,7 +165,7 @@ recheck:
{ {
/* We have a compiled function, but is it still valid? */ /* We have a compiled function, but is it still valid? */
if (function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) && 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; function_valid = true;
else else
{ {
...@@ -355,7 +355,7 @@ do_compile(FunctionCallInfo fcinfo, ...@@ -355,7 +355,7 @@ do_compile(FunctionCallInfo fcinfo,
function->fn_name = pstrdup(NameStr(procStruct->proname)); function->fn_name = pstrdup(NameStr(procStruct->proname));
function->fn_oid = fcinfo->flinfo->fn_oid; function->fn_oid = fcinfo->flinfo->fn_oid;
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); 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_functype = functype;
function->fn_cxt = func_cxt; function->fn_cxt = func_cxt;
function->out_param_varno = -1; /* set up for no OUT param */ function->out_param_varno = -1; /* set up for no OUT param */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 ...@@ -547,7 +547,7 @@ typedef struct PLpgSQL_function
char *fn_name; char *fn_name;
Oid fn_oid; Oid fn_oid;
TransactionId fn_xmin; TransactionId fn_xmin;
CommandId fn_cmin; ItemPointerData fn_tid;
int fn_functype; int fn_functype;
PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */ PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */
MemoryContext fn_cxt; MemoryContext fn_cxt;
......
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * 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 ...@@ -123,7 +123,7 @@ typedef struct PLyProcedure
char *proname; /* SQL name of procedure */ char *proname; /* SQL name of procedure */
char *pyname; /* Python name of procedure */ char *pyname; /* Python name of procedure */
TransactionId fn_xmin; TransactionId fn_xmin;
CommandId fn_cmin; ItemPointerData fn_tid;
bool fn_readonly; bool fn_readonly;
PLyTypeInfo result; /* also used to store info for trigger tuple PLyTypeInfo result; /* also used to store info for trigger tuple
* type */ * type */
...@@ -1100,7 +1100,7 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid) ...@@ -1100,7 +1100,7 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
elog(FATAL, "proc->me != plproc"); elog(FATAL, "proc->me != plproc");
/* did we find an up-to-date cache entry? */ /* did we find an up-to-date cache entry? */
if (proc->fn_xmin != HeapTupleHeaderGetXmin(procTup->t_data) || 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); Py_DECREF(plproc);
proc = NULL; proc = NULL;
...@@ -1151,7 +1151,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid, ...@@ -1151,7 +1151,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
proc->proname = PLy_strdup(NameStr(procStruct->proname)); proc->proname = PLy_strdup(NameStr(procStruct->proname));
proc->pyname = PLy_strdup(procName); proc->pyname = PLy_strdup(procName);
proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); 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 */ /* Remember if function is STABLE/IMMUTABLE */
proc->fn_readonly = proc->fn_readonly =
(procStruct->provolatile != PROVOLATILE_VOLATILE); (procStruct->provolatile != PROVOLATILE_VOLATILE);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* pltcl.c - PostgreSQL support for Tcl as * pltcl.c - PostgreSQL support for Tcl as
* procedural language (PL) * 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 ...@@ -76,7 +76,7 @@ typedef struct pltcl_proc_desc
{ {
char *proname; char *proname;
TransactionId fn_xmin; TransactionId fn_xmin;
CommandId fn_cmin; ItemPointerData fn_tid;
bool fn_readonly; bool fn_readonly;
bool lanpltrusted; bool lanpltrusted;
FmgrInfo result_in_func; FmgrInfo result_in_func;
...@@ -962,7 +962,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid) ...@@ -962,7 +962,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent); prodesc = (pltcl_proc_desc *) Tcl_GetHashValue(hashent);
uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) && uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)); ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
if (!uptodate) if (!uptodate)
{ {
...@@ -1004,7 +1004,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid) ...@@ -1004,7 +1004,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
MemSet(prodesc, 0, sizeof(pltcl_proc_desc)); MemSet(prodesc, 0, sizeof(pltcl_proc_desc));
prodesc->proname = strdup(internal_proname); prodesc->proname = strdup(internal_proname);
prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); 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 */ /* Remember if function is STABLE/IMMUTABLE */
prodesc->fn_readonly = prodesc->fn_readonly =
......
--
-- 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)
...@@ -5,14 +5,15 @@ ...@@ -5,14 +5,15 @@
-- This test tries to verify that WITHOUT OIDS actually saves space. -- 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 -- 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. -- 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, CREATE TABLE wi (i INT,
n1 int, n2 int, n3 int, n4 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, CREATE TABLE wo (i INT,
n1 int, n2 int, n3 int, n4 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 wi VALUES (1); -- 1
INSERT INTO wo SELECT i FROM wi; -- 1 INSERT INTO wo SELECT i FROM wi; -- 1
INSERT INTO wo SELECT i+1 FROM wi; -- 1+1=2 INSERT INTO wo SELECT i+1 FROM wi; -- 1+1=2
......
# ---------- # ----------
# The first group of parallel test # 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 test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid
...@@ -69,7 +69,7 @@ test: misc ...@@ -69,7 +69,7 @@ test: misc
# ---------- # ----------
# The fifth group of parallel test # 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 # The sixth group of parallel test
......
# $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. # This should probably be in an order similar to parallel_schedule.
test: boolean test: boolean
test: char test: char
...@@ -88,6 +88,7 @@ test: foreign_key ...@@ -88,6 +88,7 @@ test: foreign_key
test: cluster test: cluster
test: dependency test: dependency
test: guc test: guc
test: combocid
test: limit test: limit
test: plpgsql test: plpgsql
test: copy2 test: copy2
......
--
-- 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;
...@@ -6,14 +6,15 @@ ...@@ -6,14 +6,15 @@
-- This test tries to verify that WITHOUT OIDS actually saves space. -- 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 -- 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. -- 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, CREATE TABLE wi (i INT,
n1 int, n2 int, n3 int, n4 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, CREATE TABLE wo (i INT,
n1 int, n2 int, n3 int, n4 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 wi VALUES (1); -- 1
INSERT INTO wo SELECT i FROM wi; -- 1 INSERT INTO wo SELECT i FROM wi; -- 1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment