mirror of
https://github.com/postgres/postgres.git
synced 2025-10-25 13:17:41 +03: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:
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user