Header And Logo

PostgreSQL
| The world's most advanced open source database.

combocid.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * combocid.c
00004  *    Combo command ID support routines
00005  *
00006  * Before version 8.3, HeapTupleHeaderData had separate fields for cmin
00007  * and cmax.  To reduce the header size, cmin and cmax are now overlayed
00008  * in the same field in the header.  That usually works because you rarely
00009  * insert and delete a tuple in the same transaction, and we don't need
00010  * either field to remain valid after the originating transaction exits.
00011  * To make it work when the inserting transaction does delete the tuple,
00012  * we create a "combo" command ID and store that in the tuple header
00013  * instead of cmin and cmax. The combo command ID can be mapped to the
00014  * real cmin and cmax using a backend-private array, which is managed by
00015  * this module.
00016  *
00017  * To allow reusing existing combo cids, we also keep a hash table that
00018  * maps cmin,cmax pairs to combo cids.  This keeps the data structure size
00019  * reasonable in most cases, since the number of unique pairs used by any
00020  * one transaction is likely to be small.
00021  *
00022  * With a 32-bit combo command id we can represent 2^32 distinct cmin,cmax
00023  * combinations. In the most perverse case where each command deletes a tuple
00024  * generated by every previous command, the number of combo command ids
00025  * required for N commands is N*(N+1)/2.  That means that in the worst case,
00026  * that's enough for 92682 commands.  In practice, you'll run out of memory
00027  * and/or disk space way before you reach that limit.
00028  *
00029  * The array and hash table are kept in TopTransactionContext, and are
00030  * destroyed at the end of each transaction.
00031  *
00032  *
00033  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00034  * Portions Copyright (c) 1994, Regents of the University of California
00035  *
00036  * IDENTIFICATION
00037  *    src/backend/utils/time/combocid.c
00038  *
00039  *-------------------------------------------------------------------------
00040  */
00041 
00042 #include "postgres.h"
00043 
00044 #include "access/htup_details.h"
00045 #include "access/xact.h"
00046 #include "utils/combocid.h"
00047 #include "utils/hsearch.h"
00048 #include "utils/memutils.h"
00049 
00050 
00051 /* Hash table to lookup combo cids by cmin and cmax */
00052 static HTAB *comboHash = NULL;
00053 
00054 /* Key and entry structures for the hash table */
00055 typedef struct
00056 {
00057     CommandId   cmin;
00058     CommandId   cmax;
00059 } ComboCidKeyData;
00060 
00061 typedef ComboCidKeyData *ComboCidKey;
00062 
00063 typedef struct
00064 {
00065     ComboCidKeyData key;
00066     CommandId   combocid;
00067 } ComboCidEntryData;
00068 
00069 typedef ComboCidEntryData *ComboCidEntry;
00070 
00071 /* Initial size of the hash table */
00072 #define CCID_HASH_SIZE          100
00073 
00074 
00075 /*
00076  * An array of cmin,cmax pairs, indexed by combo command id.
00077  * To convert a combo cid to cmin and cmax, you do a simple array lookup.
00078  */
00079 static ComboCidKey comboCids = NULL;
00080 static int  usedComboCids = 0;  /* number of elements in comboCids */
00081 static int  sizeComboCids = 0;  /* allocated size of array */
00082 
00083 /* Initial size of the array */
00084 #define CCID_ARRAY_SIZE         100
00085 
00086 
00087 /* prototypes for internal functions */
00088 static CommandId GetComboCommandId(CommandId cmin, CommandId cmax);
00089 static CommandId GetRealCmin(CommandId combocid);
00090 static CommandId GetRealCmax(CommandId combocid);
00091 
00092 
00093 /**** External API ****/
00094 
00095 /*
00096  * GetCmin and GetCmax assert that they are only called in situations where
00097  * they make sense, that is, can deliver a useful answer.  If you have
00098  * reason to examine a tuple's t_cid field from a transaction other than
00099  * the originating one, use HeapTupleHeaderGetRawCommandId() directly.
00100  */
00101 
00102 CommandId
00103 HeapTupleHeaderGetCmin(HeapTupleHeader tup)
00104 {
00105     CommandId   cid = HeapTupleHeaderGetRawCommandId(tup);
00106 
00107     Assert(!(tup->t_infomask & HEAP_MOVED));
00108     Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tup)));
00109 
00110     if (tup->t_infomask & HEAP_COMBOCID)
00111         return GetRealCmin(cid);
00112     else
00113         return cid;
00114 }
00115 
00116 CommandId
00117 HeapTupleHeaderGetCmax(HeapTupleHeader tup)
00118 {
00119     CommandId   cid = HeapTupleHeaderGetRawCommandId(tup);
00120 
00121     Assert(!(tup->t_infomask & HEAP_MOVED));
00122     Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tup)));
00123 
00124     if (tup->t_infomask & HEAP_COMBOCID)
00125         return GetRealCmax(cid);
00126     else
00127         return cid;
00128 }
00129 
00130 /*
00131  * Given a tuple we are about to delete, determine the correct value to store
00132  * into its t_cid field.
00133  *
00134  * If we don't need a combo CID, *cmax is unchanged and *iscombo is set to
00135  * FALSE.  If we do need one, *cmax is replaced by a combo CID and *iscombo
00136  * is set to TRUE.
00137  *
00138  * The reason this is separate from the actual HeapTupleHeaderSetCmax()
00139  * operation is that this could fail due to out-of-memory conditions.  Hence
00140  * we need to do this before entering the critical section that actually
00141  * changes the tuple in shared buffers.
00142  */
00143 void
00144 HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
00145                           CommandId *cmax,
00146                           bool *iscombo)
00147 {
00148     /*
00149      * If we're marking a tuple deleted that was inserted by (any
00150      * subtransaction of) our transaction, we need to use a combo command id.
00151      * Test for HEAP_XMIN_COMMITTED first, because it's cheaper than a
00152      * TransactionIdIsCurrentTransactionId call.
00153      */
00154     if (!(tup->t_infomask & HEAP_XMIN_COMMITTED) &&
00155         TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tup)))
00156     {
00157         CommandId   cmin = HeapTupleHeaderGetCmin(tup);
00158 
00159         *cmax = GetComboCommandId(cmin, *cmax);
00160         *iscombo = true;
00161     }
00162     else
00163     {
00164         *iscombo = false;
00165     }
00166 }
00167 
00168 /*
00169  * Combo command ids are only interesting to the inserting and deleting
00170  * transaction, so we can forget about them at the end of transaction.
00171  */
00172 void
00173 AtEOXact_ComboCid(void)
00174 {
00175     /*
00176      * Don't bother to pfree. These are allocated in TopTransactionContext, so
00177      * they're going to go away at the end of transaction anyway.
00178      */
00179     comboHash = NULL;
00180 
00181     comboCids = NULL;
00182     usedComboCids = 0;
00183     sizeComboCids = 0;
00184 }
00185 
00186 
00187 /**** Internal routines ****/
00188 
00189 /*
00190  * Get a combo command id that maps to cmin and cmax.
00191  *
00192  * We try to reuse old combo command ids when possible.
00193  */
00194 static CommandId
00195 GetComboCommandId(CommandId cmin, CommandId cmax)
00196 {
00197     CommandId   combocid;
00198     ComboCidKeyData key;
00199     ComboCidEntry entry;
00200     bool        found;
00201 
00202     /*
00203      * Create the hash table and array the first time we need to use combo
00204      * cids in the transaction.
00205      */
00206     if (comboHash == NULL)
00207     {
00208         HASHCTL     hash_ctl;
00209 
00210         memset(&hash_ctl, 0, sizeof(hash_ctl));
00211         hash_ctl.keysize = sizeof(ComboCidKeyData);
00212         hash_ctl.entrysize = sizeof(ComboCidEntryData);
00213         hash_ctl.hash = tag_hash;
00214         hash_ctl.hcxt = TopTransactionContext;
00215 
00216         comboHash = hash_create("Combo CIDs",
00217                                 CCID_HASH_SIZE,
00218                                 &hash_ctl,
00219                                 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
00220 
00221         comboCids = (ComboCidKeyData *)
00222             MemoryContextAlloc(TopTransactionContext,
00223                                sizeof(ComboCidKeyData) * CCID_ARRAY_SIZE);
00224         sizeComboCids = CCID_ARRAY_SIZE;
00225         usedComboCids = 0;
00226     }
00227 
00228     /* Lookup or create a hash entry with the desired cmin/cmax */
00229 
00230     /* We assume there is no struct padding in ComboCidKeyData! */
00231     key.cmin = cmin;
00232     key.cmax = cmax;
00233     entry = (ComboCidEntry) hash_search(comboHash,
00234                                         (void *) &key,
00235                                         HASH_ENTER,
00236                                         &found);
00237 
00238     if (found)
00239     {
00240         /* Reuse an existing combo cid */
00241         return entry->combocid;
00242     }
00243 
00244     /*
00245      * We have to create a new combo cid. Check that there's room for it in
00246      * the array, and grow it if there isn't.
00247      */
00248     if (usedComboCids >= sizeComboCids)
00249     {
00250         /* We need to grow the array */
00251         int         newsize = sizeComboCids * 2;
00252 
00253         comboCids = (ComboCidKeyData *)
00254             repalloc(comboCids, sizeof(ComboCidKeyData) * newsize);
00255         sizeComboCids = newsize;
00256     }
00257 
00258     combocid = usedComboCids;
00259 
00260     comboCids[combocid].cmin = cmin;
00261     comboCids[combocid].cmax = cmax;
00262     usedComboCids++;
00263 
00264     entry->combocid = combocid;
00265 
00266     return combocid;
00267 }
00268 
00269 static CommandId
00270 GetRealCmin(CommandId combocid)
00271 {
00272     Assert(combocid < usedComboCids);
00273     return comboCids[combocid].cmin;
00274 }
00275 
00276 static CommandId
00277 GetRealCmax(CommandId combocid)
00278 {
00279     Assert(combocid < usedComboCids);
00280     return comboCids[combocid].cmax;
00281 }