mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-24 18:55:04 +08:00
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.
This commit is contained in:
parent
acb3416686
commit
c398300330
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -38,11 +38,12 @@
|
||||
#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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
282
src/backend/utils/time/combocid.c
Normal file
282
src/backend/utils/time/combocid.c
Normal file
@ -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;
|
||||
}
|
@ -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
|
||||
@ -65,17 +65,18 @@
|
||||
* 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,
|
||||
@ -103,14 +104,13 @@
|
||||
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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
25
src/include/utils/combocid.h
Normal file
25
src/include/utils/combocid.h
Normal file
@ -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 */
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 =
|
||||
|
242
src/test/regress/expected/combocid.out
Normal file
242
src/test/regress/expected/combocid.out
Normal file
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
93
src/test/regress/sql/combocid.sql
Normal file
93
src/test/regress/sql/combocid.sql
Normal file
@ -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;
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user