LTP GCOV extension - code coverage report
Current view: directory - access/heap - rewriteheap.c
Test: unnamed
Date: 2008-07-03 Instrumented lines: 123
Code covered: 82.9 % Executed lines: 102
Legend: not executed executed

       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * rewriteheap.c
       4                 :  *        Support functions to rewrite tables.
       5                 :  *
       6                 :  * These functions provide a facility to completely rewrite a heap, while
       7                 :  * preserving visibility information and update chains.
       8                 :  *
       9                 :  * INTERFACE
      10                 :  *
      11                 :  * The caller is responsible for creating the new heap, all catalog
      12                 :  * changes, supplying the tuples to be written to the new heap, and
      13                 :  * rebuilding indexes.  The caller must hold AccessExclusiveLock on the
      14                 :  * target table, because we assume no one else is writing into it.
      15                 :  *
      16                 :  * To use the facility:
      17                 :  *
      18                 :  * begin_heap_rewrite
      19                 :  * while (fetch next tuple)
      20                 :  * {
      21                 :  *         if (tuple is dead)
      22                 :  *                 rewrite_heap_dead_tuple
      23                 :  *         else
      24                 :  *         {
      25                 :  *                 // do any transformations here if required
      26                 :  *                 rewrite_heap_tuple
      27                 :  *         }
      28                 :  * }
      29                 :  * end_heap_rewrite
      30                 :  *
      31                 :  * The contents of the new relation shouldn't be relied on until after
      32                 :  * end_heap_rewrite is called.
      33                 :  *
      34                 :  *
      35                 :  * IMPLEMENTATION
      36                 :  *
      37                 :  * This would be a fairly trivial affair, except that we need to maintain
      38                 :  * the ctid chains that link versions of an updated tuple together.
      39                 :  * Since the newly stored tuples will have tids different from the original
      40                 :  * ones, if we just copied t_ctid fields to the new table the links would
      41                 :  * be wrong.  When we are required to copy a (presumably recently-dead or
      42                 :  * delete-in-progress) tuple whose ctid doesn't point to itself, we have
      43                 :  * to substitute the correct ctid instead.
      44                 :  *
      45                 :  * For each ctid reference from A -> B, we might encounter either A first
      46                 :  * or B first.  (Note that a tuple in the middle of a chain is both A and B
      47                 :  * of different pairs.)
      48                 :  *
      49                 :  * If we encounter A first, we'll store the tuple in the unresolved_tups
      50                 :  * hash table. When we later encounter B, we remove A from the hash table,
      51                 :  * fix the ctid to point to the new location of B, and insert both A and B
      52                 :  * to the new heap.
      53                 :  *
      54                 :  * If we encounter B first, we can insert B to the new heap right away.
      55                 :  * We then add an entry to the old_new_tid_map hash table showing B's
      56                 :  * original tid (in the old heap) and new tid (in the new heap).
      57                 :  * When we later encounter A, we get the new location of B from the table,
      58                 :  * and can write A immediately with the correct ctid.
      59                 :  *
      60                 :  * Entries in the hash tables can be removed as soon as the later tuple
      61                 :  * is encountered.      That helps to keep the memory usage down.  At the end,
      62                 :  * both tables are usually empty; we should have encountered both A and B
      63                 :  * of each pair.  However, it's possible for A to be RECENTLY_DEAD and B
      64                 :  * entirely DEAD according to HeapTupleSatisfiesVacuum, because the test
      65                 :  * for deadness using OldestXmin is not exact.  In such a case we might
      66                 :  * encounter B first, and skip it, and find A later.  Then A would be added
      67                 :  * to unresolved_tups, and stay there until end of the rewrite.  Since
      68                 :  * this case is very unusual, we don't worry about the memory usage.
      69                 :  *
      70                 :  * Using in-memory hash tables means that we use some memory for each live
      71                 :  * update chain in the table, from the time we find one end of the
      72                 :  * reference until we find the other end.  That shouldn't be a problem in
      73                 :  * practice, but if you do something like an UPDATE without a where-clause
      74                 :  * on a large table, and then run CLUSTER in the same transaction, you
      75                 :  * could run out of memory.  It doesn't seem worthwhile to add support for
      76                 :  * spill-to-disk, as there shouldn't be that many RECENTLY_DEAD tuples in a
      77                 :  * table under normal circumstances.  Furthermore, in the typical scenario
      78                 :  * of CLUSTERing on an unchanging key column, we'll see all the versions
      79                 :  * of a given tuple together anyway, and so the peak memory usage is only
      80                 :  * proportional to the number of RECENTLY_DEAD versions of a single row, not
      81                 :  * in the whole table.  Note that if we do fail halfway through a CLUSTER,
      82                 :  * the old table is still valid, so failure is not catastrophic.
      83                 :  *
      84                 :  * We can't use the normal heap_insert function to insert into the new
      85                 :  * heap, because heap_insert overwrites the visibility information.
      86                 :  * We use a special-purpose raw_heap_insert function instead, which
      87                 :  * is optimized for bulk inserting a lot of tuples, knowing that we have
      88                 :  * exclusive access to the heap.  raw_heap_insert builds new pages in
      89                 :  * local storage.  When a page is full, or at the end of the process,
      90                 :  * we insert it to WAL as a single record and then write it to disk
      91                 :  * directly through smgr.  Note, however, that any data sent to the new
      92                 :  * heap's TOAST table will go through the normal bufmgr.
      93                 :  *
      94                 :  *
      95                 :  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
      96                 :  * Portions Copyright (c) 1994-5, Regents of the University of California
      97                 :  *
      98                 :  * IDENTIFICATION
      99                 :  *        $PostgreSQL: pgsql/src/backend/access/heap/rewriteheap.c,v 1.9 2007/11/15 22:25:15 momjian Exp $
     100                 :  *
     101                 :  *-------------------------------------------------------------------------
     102                 :  */
     103                 : #include "postgres.h"
     104                 : 
     105                 : #include "access/heapam.h"
     106                 : #include "access/rewriteheap.h"
     107                 : #include "access/transam.h"
     108                 : #include "access/tuptoaster.h"
     109                 : #include "storage/smgr.h"
     110                 : #include "utils/memutils.h"
     111                 : 
     112                 : 
     113                 : /*
     114                 :  * State associated with a rewrite operation. This is opaque to the user
     115                 :  * of the rewrite facility.
     116                 :  */
     117                 : typedef struct RewriteStateData
     118                 : {
     119                 :         Relation        rs_new_rel;             /* destination heap */
     120                 :         Page            rs_buffer;              /* page currently being built */
     121                 :         BlockNumber rs_blockno;         /* block where page will go */
     122                 :         bool            rs_buffer_valid;        /* T if any tuples in buffer */
     123                 :         bool            rs_use_wal;             /* must we WAL-log inserts? */
     124                 :         TransactionId rs_oldest_xmin;           /* oldest xmin used by caller to
     125                 :                                                                                  * determine tuple visibility */
     126                 :         TransactionId rs_freeze_xid;/* Xid that will be used as freeze cutoff
     127                 :                                                                  * point */
     128                 :         MemoryContext rs_cxt;           /* for hash tables and entries and tuples in
     129                 :                                                                  * them */
     130                 :         HTAB       *rs_unresolved_tups;         /* unmatched A tuples */
     131                 :         HTAB       *rs_old_new_tid_map;         /* unmatched B tuples */
     132                 : } RewriteStateData;
     133                 : 
     134                 : /*
     135                 :  * The lookup keys for the hash tables are tuple TID and xmin (we must check
     136                 :  * both to avoid false matches from dead tuples).  Beware that there is
     137                 :  * probably some padding space in this struct; it must be zeroed out for
     138                 :  * correct hashtable operation.
     139                 :  */
     140                 : typedef struct
     141                 : {
     142                 :         TransactionId xmin;                     /* tuple xmin */
     143                 :         ItemPointerData tid;            /* tuple location in old heap */
     144                 : } TidHashKey;
     145                 : 
     146                 : /*
     147                 :  * Entry structures for the hash tables
     148                 :  */
     149                 : typedef struct
     150                 : {
     151                 :         TidHashKey      key;                    /* expected xmin/old location of B tuple */
     152                 :         ItemPointerData old_tid;        /* A's location in the old heap */
     153                 :         HeapTuple       tuple;                  /* A's tuple contents */
     154                 : } UnresolvedTupData;
     155                 : 
     156                 : typedef UnresolvedTupData *UnresolvedTup;
     157                 : 
     158                 : typedef struct
     159                 : {
     160                 :         TidHashKey      key;                    /* actual xmin/old location of B tuple */
     161                 :         ItemPointerData new_tid;        /* where we put it in the new heap */
     162                 : } OldToNewMappingData;
     163                 : 
     164                 : typedef OldToNewMappingData *OldToNewMapping;
     165                 : 
     166                 : 
     167                 : /* prototypes for internal functions */
     168                 : static void raw_heap_insert(RewriteState state, HeapTuple tup);
     169                 : 
     170                 : 
     171                 : /*
     172                 :  * Begin a rewrite of a table
     173                 :  *
     174                 :  * new_heap             new, locked heap relation to insert tuples to
     175                 :  * oldest_xmin  xid used by the caller to determine which tuples are dead
     176                 :  * freeze_xid   xid before which tuples will be frozen
     177                 :  * use_wal              should the inserts to the new heap be WAL-logged?
     178                 :  *
     179                 :  * Returns an opaque RewriteState, allocated in current memory context,
     180                 :  * to be used in subsequent calls to the other functions.
     181                 :  */
     182                 : RewriteState
     183                 : begin_heap_rewrite(Relation new_heap, TransactionId oldest_xmin,
     184                 :                                    TransactionId freeze_xid, bool use_wal)
     185               6 : {
     186                 :         RewriteState state;
     187                 :         MemoryContext rw_cxt;
     188                 :         MemoryContext old_cxt;
     189                 :         HASHCTL         hash_ctl;
     190                 : 
     191                 :         /*
     192                 :          * To ease cleanup, make a separate context that will contain the
     193                 :          * RewriteState struct itself plus all subsidiary data.
     194                 :          */
     195               6 :         rw_cxt = AllocSetContextCreate(CurrentMemoryContext,
     196                 :                                                                    "Table rewrite",
     197                 :                                                                    ALLOCSET_DEFAULT_MINSIZE,
     198                 :                                                                    ALLOCSET_DEFAULT_INITSIZE,
     199                 :                                                                    ALLOCSET_DEFAULT_MAXSIZE);
     200               6 :         old_cxt = MemoryContextSwitchTo(rw_cxt);
     201                 : 
     202                 :         /* Create and fill in the state struct */
     203               6 :         state = palloc0(sizeof(RewriteStateData));
     204                 : 
     205               6 :         state->rs_new_rel = new_heap;
     206               6 :         state->rs_buffer = (Page) palloc(BLCKSZ);
     207                 :         /* new_heap needn't be empty, just locked */
     208               6 :         state->rs_blockno = RelationGetNumberOfBlocks(new_heap);
     209               6 :         state->rs_buffer_valid = false;
     210               6 :         state->rs_use_wal = use_wal;
     211               6 :         state->rs_oldest_xmin = oldest_xmin;
     212               6 :         state->rs_freeze_xid = freeze_xid;
     213               6 :         state->rs_cxt = rw_cxt;
     214                 : 
     215                 :         /* Initialize hash tables used to track update chains */
     216               6 :         memset(&hash_ctl, 0, sizeof(hash_ctl));
     217               6 :         hash_ctl.keysize = sizeof(TidHashKey);
     218               6 :         hash_ctl.entrysize = sizeof(UnresolvedTupData);
     219               6 :         hash_ctl.hcxt = state->rs_cxt;
     220               6 :         hash_ctl.hash = tag_hash;
     221                 : 
     222               6 :         state->rs_unresolved_tups =
     223                 :                 hash_create("Rewrite / Unresolved ctids",
     224                 :                                         128,            /* arbitrary initial size */
     225                 :                                         &hash_ctl,
     226                 :                                         HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
     227                 : 
     228               6 :         hash_ctl.entrysize = sizeof(OldToNewMappingData);
     229                 : 
     230               6 :         state->rs_old_new_tid_map =
     231                 :                 hash_create("Rewrite / Old to new tid map",
     232                 :                                         128,            /* arbitrary initial size */
     233                 :                                         &hash_ctl,
     234                 :                                         HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
     235                 : 
     236               6 :         MemoryContextSwitchTo(old_cxt);
     237                 : 
     238               6 :         return state;
     239                 : }
     240                 : 
     241                 : /*
     242                 :  * End a rewrite.
     243                 :  *
     244                 :  * state and any other resources are freed.
     245                 :  */
     246                 : void
     247                 : end_heap_rewrite(RewriteState state)
     248               6 : {
     249                 :         HASH_SEQ_STATUS seq_status;
     250                 :         UnresolvedTup unresolved;
     251                 : 
     252                 :         /*
     253                 :          * Write any remaining tuples in the UnresolvedTups table. If we have any
     254                 :          * left, they should in fact be dead, but let's err on the safe side.
     255                 :          *
     256                 :          * XXX this really is a waste of code no?
     257                 :          */
     258               6 :         hash_seq_init(&seq_status, state->rs_unresolved_tups);
     259                 : 
     260              12 :         while ((unresolved = hash_seq_search(&seq_status)) != NULL)
     261                 :         {
     262               0 :                 ItemPointerSetInvalid(&unresolved->tuple->t_data->t_ctid);
     263               0 :                 raw_heap_insert(state, unresolved->tuple);
     264                 :         }
     265                 : 
     266                 :         /* Write the last page, if any */
     267               6 :         if (state->rs_buffer_valid)
     268                 :         {
     269               6 :                 if (state->rs_use_wal)
     270               0 :                         log_newpage(&state->rs_new_rel->rd_node,
     271                 :                                                 state->rs_blockno,
     272                 :                                                 state->rs_buffer);
     273               6 :                 RelationOpenSmgr(state->rs_new_rel);
     274               6 :                 smgrextend(state->rs_new_rel->rd_smgr, state->rs_blockno,
     275                 :                                    (char *) state->rs_buffer, true);
     276                 :         }
     277                 : 
     278                 :         /*
     279                 :          * If the rel isn't temp, must fsync before commit.  We use heap_sync to
     280                 :          * ensure that the toast table gets fsync'd too.
     281                 :          *
     282                 :          * It's obvious that we must do this when not WAL-logging. It's less
     283                 :          * obvious that we have to do it even if we did WAL-log the pages. The
     284                 :          * reason is the same as in tablecmds.c's copy_relation_data(): we're
     285                 :          * writing data that's not in shared buffers, and so a CHECKPOINT
     286                 :          * occurring during the rewriteheap operation won't have fsync'd data we
     287                 :          * wrote before the checkpoint.
     288                 :          */
     289               6 :         if (!state->rs_new_rel->rd_istemp)
     290               6 :                 heap_sync(state->rs_new_rel);
     291                 : 
     292                 :         /* Deleting the context frees everything */
     293               6 :         MemoryContextDelete(state->rs_cxt);
     294               6 : }
     295                 : 
     296                 : /*
     297                 :  * Add a tuple to the new heap.
     298                 :  *
     299                 :  * Visibility information is copied from the original tuple, except that
     300                 :  * we "freeze" very-old tuples.  Note that since we scribble on new_tuple,
     301                 :  * it had better be temp storage not a pointer to the original tuple.
     302                 :  *
     303                 :  * state                opaque state as returned by begin_heap_rewrite
     304                 :  * old_tuple    original tuple in the old heap
     305                 :  * new_tuple    new, rewritten tuple to be inserted to new heap
     306                 :  */
     307                 : void
     308                 : rewrite_heap_tuple(RewriteState state,
     309                 :                                    HeapTuple old_tuple, HeapTuple new_tuple)
     310              56 : {
     311                 :         MemoryContext old_cxt;
     312                 :         ItemPointerData old_tid;
     313                 :         TidHashKey      hashkey;
     314                 :         bool            found;
     315                 :         bool            free_new;
     316                 : 
     317              56 :         old_cxt = MemoryContextSwitchTo(state->rs_cxt);
     318                 : 
     319                 :         /*
     320                 :          * Copy the original tuple's visibility information into new_tuple.
     321                 :          *
     322                 :          * XXX we might later need to copy some t_infomask2 bits, too? Right now,
     323                 :          * we intentionally clear the HOT status bits.
     324                 :          */
     325              56 :         memcpy(&new_tuple->t_data->t_choice.t_heap,
     326                 :                    &old_tuple->t_data->t_choice.t_heap,
     327                 :                    sizeof(HeapTupleFields));
     328                 : 
     329              56 :         new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
     330              56 :         new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
     331              56 :         new_tuple->t_data->t_infomask |=
     332                 :                 old_tuple->t_data->t_infomask & HEAP_XACT_MASK;
     333                 : 
     334                 :         /*
     335                 :          * While we have our hands on the tuple, we may as well freeze any
     336                 :          * very-old xmin or xmax, so that future VACUUM effort can be saved.
     337                 :          *
     338                 :          * Note we abuse heap_freeze_tuple() a bit here, since it's expecting to
     339                 :          * be given a pointer to a tuple in a disk buffer.      It happens though that
     340                 :          * we can get the right things to happen by passing InvalidBuffer for the
     341                 :          * buffer.
     342                 :          */
     343              56 :         heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid, InvalidBuffer);
     344                 : 
     345                 :         /*
     346                 :          * Invalid ctid means that ctid should point to the tuple itself. We'll
     347                 :          * override it later if the tuple is part of an update chain.
     348                 :          */
     349              56 :         ItemPointerSetInvalid(&new_tuple->t_data->t_ctid);
     350                 : 
     351                 :         /*
     352                 :          * If the tuple has been updated, check the old-to-new mapping hash table.
     353                 :          */
     354              56 :         if (!(old_tuple->t_data->t_infomask & (HEAP_XMAX_INVALID |
     355                 :                                                                                    HEAP_IS_LOCKED)) &&
     356                 :                 !(ItemPointerEquals(&(old_tuple->t_self),
     357                 :                                                         &(old_tuple->t_data->t_ctid))))
     358                 :         {
     359                 :                 OldToNewMapping mapping;
     360                 : 
     361               5 :                 memset(&hashkey, 0, sizeof(hashkey));
     362               5 :                 hashkey.xmin = HeapTupleHeaderGetXmax(old_tuple->t_data);
     363               5 :                 hashkey.tid = old_tuple->t_data->t_ctid;
     364                 : 
     365               5 :                 mapping = (OldToNewMapping)
     366                 :                         hash_search(state->rs_old_new_tid_map, &hashkey,
     367                 :                                                 HASH_FIND, NULL);
     368                 : 
     369               5 :                 if (mapping != NULL)
     370                 :                 {
     371                 :                         /*
     372                 :                          * We've already copied the tuple that t_ctid points to, so we can
     373                 :                          * set the ctid of this tuple to point to the new location, and
     374                 :                          * insert it right away.
     375                 :                          */
     376               1 :                         new_tuple->t_data->t_ctid = mapping->new_tid;
     377                 : 
     378                 :                         /* We don't need the mapping entry anymore */
     379               1 :                         hash_search(state->rs_old_new_tid_map, &hashkey,
     380                 :                                                 HASH_REMOVE, &found);
     381                 :                         Assert(found);
     382                 :                 }
     383                 :                 else
     384                 :                 {
     385                 :                         /*
     386                 :                          * We haven't seen the tuple t_ctid points to yet. Stash this
     387                 :                          * tuple into unresolved_tups to be written later.
     388                 :                          */
     389                 :                         UnresolvedTup unresolved;
     390                 : 
     391               4 :                         unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
     392                 :                                                                          HASH_ENTER, &found);
     393                 :                         Assert(!found);
     394                 : 
     395               4 :                         unresolved->old_tid = old_tuple->t_self;
     396               4 :                         unresolved->tuple = heap_copytuple(new_tuple);
     397                 : 
     398                 :                         /*
     399                 :                          * We can't do anything more now, since we don't know where the
     400                 :                          * tuple will be written.
     401                 :                          */
     402               4 :                         MemoryContextSwitchTo(old_cxt);
     403               4 :                         return;
     404                 :                 }
     405                 :         }
     406                 : 
     407                 :         /*
     408                 :          * Now we will write the tuple, and then check to see if it is the B tuple
     409                 :          * in any new or known pair.  When we resolve a known pair, we will be
     410                 :          * able to write that pair's A tuple, and then we have to check if it
     411                 :          * resolves some other pair.  Hence, we need a loop here.
     412                 :          */
     413              52 :         old_tid = old_tuple->t_self;
     414              52 :         free_new = false;
     415                 : 
     416                 :         for (;;)
     417                 :         {
     418                 :                 ItemPointerData new_tid;
     419                 : 
     420                 :                 /* Insert the tuple and find out where it's put in new_heap */
     421              56 :                 raw_heap_insert(state, new_tuple);
     422              56 :                 new_tid = new_tuple->t_self;
     423                 : 
     424                 :                 /*
     425                 :                  * If the tuple is the updated version of a row, and the prior version
     426                 :                  * wouldn't be DEAD yet, then we need to either resolve the prior
     427                 :                  * version (if it's waiting in rs_unresolved_tups), or make an entry
     428                 :                  * in rs_old_new_tid_map (so we can resolve it when we do see it). The
     429                 :                  * previous tuple's xmax would equal this one's xmin, so it's
     430                 :                  * RECENTLY_DEAD if and only if the xmin is not before OldestXmin.
     431                 :                  */
     432              56 :                 if ((new_tuple->t_data->t_infomask & HEAP_UPDATED) &&
     433                 :                         !TransactionIdPrecedes(HeapTupleHeaderGetXmin(new_tuple->t_data),
     434                 :                                                                    state->rs_oldest_xmin))
     435                 :                 {
     436                 :                         /*
     437                 :                          * Okay, this is B in an update pair.  See if we've seen A.
     438                 :                          */
     439                 :                         UnresolvedTup unresolved;
     440                 : 
     441               5 :                         memset(&hashkey, 0, sizeof(hashkey));
     442               5 :                         hashkey.xmin = HeapTupleHeaderGetXmin(new_tuple->t_data);
     443               5 :                         hashkey.tid = old_tid;
     444                 : 
     445               5 :                         unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
     446                 :                                                                          HASH_FIND, NULL);
     447                 : 
     448               5 :                         if (unresolved != NULL)
     449                 :                         {
     450                 :                                 /*
     451                 :                                  * We have seen and memorized the previous tuple already. Now
     452                 :                                  * that we know where we inserted the tuple its t_ctid points
     453                 :                                  * to, fix its t_ctid and insert it to the new heap.
     454                 :                                  */
     455               4 :                                 if (free_new)
     456               2 :                                         heap_freetuple(new_tuple);
     457               4 :                                 new_tuple = unresolved->tuple;
     458               4 :                                 free_new = true;
     459               4 :                                 old_tid = unresolved->old_tid;
     460               4 :                                 new_tuple->t_data->t_ctid = new_tid;
     461                 : 
     462                 :                                 /*
     463                 :                                  * We don't need the hash entry anymore, but don't free its
     464                 :                                  * tuple just yet.
     465                 :                                  */
     466               4 :                                 hash_search(state->rs_unresolved_tups, &hashkey,
     467                 :                                                         HASH_REMOVE, &found);
     468                 :                                 Assert(found);
     469                 : 
     470                 :                                 /* loop back to insert the previous tuple in the chain */
     471               4 :                                 continue;
     472                 :                         }
     473                 :                         else
     474                 :                         {
     475                 :                                 /*
     476                 :                                  * Remember the new tid of this tuple. We'll use it to set the
     477                 :                                  * ctid when we find the previous tuple in the chain.
     478                 :                                  */
     479                 :                                 OldToNewMapping mapping;
     480                 : 
     481               1 :                                 mapping = hash_search(state->rs_old_new_tid_map, &hashkey,
     482                 :                                                                           HASH_ENTER, &found);
     483                 :                                 Assert(!found);
     484                 : 
     485               1 :                                 mapping->new_tid = new_tid;
     486                 :                         }
     487                 :                 }
     488                 : 
     489                 :                 /* Done with this (chain of) tuples, for now */
     490              52 :                 if (free_new)
     491               2 :                         heap_freetuple(new_tuple);
     492                 :                 break;
     493                 :         }
     494                 : 
     495              52 :         MemoryContextSwitchTo(old_cxt);
     496                 : }
     497                 : 
     498                 : /*
     499                 :  * Register a dead tuple with an ongoing rewrite. Dead tuples are not
     500                 :  * copied to the new table, but we still make note of them so that we
     501                 :  * can release some resources earlier.
     502                 :  */
     503                 : void
     504                 : rewrite_heap_dead_tuple(RewriteState state, HeapTuple old_tuple)
     505               0 : {
     506                 :         /*
     507                 :          * If we have already seen an earlier tuple in the update chain that
     508                 :          * points to this tuple, let's forget about that earlier tuple. It's in
     509                 :          * fact dead as well, our simple xmax < OldestXmin test in
     510                 :          * HeapTupleSatisfiesVacuum just wasn't enough to detect it. It happens
     511                 :          * when xmin of a tuple is greater than xmax, which sounds
     512                 :          * counter-intuitive but is perfectly valid.
     513                 :          *
     514                 :          * We don't bother to try to detect the situation the other way round,
     515                 :          * when we encounter the dead tuple first and then the recently dead one
     516                 :          * that points to it. If that happens, we'll have some unmatched entries
     517                 :          * in the UnresolvedTups hash table at the end. That can happen anyway,
     518                 :          * because a vacuum might have removed the dead tuple in the chain before
     519                 :          * us.
     520                 :          */
     521                 :         UnresolvedTup unresolved;
     522                 :         TidHashKey      hashkey;
     523                 :         bool            found;
     524                 : 
     525               0 :         memset(&hashkey, 0, sizeof(hashkey));
     526               0 :         hashkey.xmin = HeapTupleHeaderGetXmin(old_tuple->t_data);
     527               0 :         hashkey.tid = old_tuple->t_self;
     528                 : 
     529               0 :         unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
     530                 :                                                          HASH_FIND, NULL);
     531                 : 
     532               0 :         if (unresolved != NULL)
     533                 :         {
     534                 :                 /* Need to free the contained tuple as well as the hashtable entry */
     535               0 :                 heap_freetuple(unresolved->tuple);
     536               0 :                 hash_search(state->rs_unresolved_tups, &hashkey,
     537                 :                                         HASH_REMOVE, &found);
     538                 :                 Assert(found);
     539                 :         }
     540               0 : }
     541                 : 
     542                 : /*
     543                 :  * Insert a tuple to the new relation.  This has to track heap_insert
     544                 :  * and its subsidiary functions!
     545                 :  *
     546                 :  * t_self of the tuple is set to the new TID of the tuple. If t_ctid of the
     547                 :  * tuple is invalid on entry, it's replaced with the new TID as well (in
     548                 :  * the inserted data only, not in the caller's copy).
     549                 :  */
     550                 : static void
     551                 : raw_heap_insert(RewriteState state, HeapTuple tup)
     552              56 : {
     553              56 :         Page            page = state->rs_buffer;
     554                 :         Size            pageFreeSpace,
     555                 :                                 saveFreeSpace;
     556                 :         Size            len;
     557                 :         OffsetNumber newoff;
     558                 :         HeapTuple       heaptup;
     559                 : 
     560                 :         /*
     561                 :          * If the new tuple is too big for storage or contains already toasted
     562                 :          * out-of-line attributes from some other relation, invoke the toaster.
     563                 :          *
     564                 :          * Note: below this point, heaptup is the data we actually intend to store
     565                 :          * into the relation; tup is the caller's original untoasted data.
     566                 :          */
     567              56 :         if (state->rs_new_rel->rd_rel->relkind == RELKIND_TOASTVALUE)
     568                 :         {
     569                 :                 /* toast table entries should never be recursively toasted */
     570                 :                 Assert(!HeapTupleHasExternal(tup));
     571               0 :                 heaptup = tup;
     572                 :         }
     573              57 :         else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
     574               1 :                 heaptup = toast_insert_or_update(state->rs_new_rel, tup, NULL,
     575                 :                                                                                  state->rs_use_wal, false);
     576                 :         else
     577              55 :                 heaptup = tup;
     578                 : 
     579              56 :         len = MAXALIGN(heaptup->t_len);              /* be conservative */
     580                 : 
     581                 :         /*
     582                 :          * If we're gonna fail for oversize tuple, do it right away
     583                 :          */
     584              56 :         if (len > MaxHeapTupleSize)
     585               0 :                 ereport(ERROR,
     586                 :                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     587                 :                                  errmsg("row is too big: size %lu, maximum size %lu",
     588                 :                                                 (unsigned long) len,
     589                 :                                                 (unsigned long) MaxHeapTupleSize)));
     590                 : 
     591                 :         /* Compute desired extra freespace due to fillfactor option */
     592              56 :         saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
     593                 :                                                                                                    HEAP_DEFAULT_FILLFACTOR);
     594                 : 
     595                 :         /* Now we can check to see if there's enough free space already. */
     596              56 :         if (state->rs_buffer_valid)
     597                 :         {
     598              50 :                 pageFreeSpace = PageGetHeapFreeSpace(page);
     599                 : 
     600              50 :                 if (len + saveFreeSpace > pageFreeSpace)
     601                 :                 {
     602                 :                         /* Doesn't fit, so write out the existing page */
     603                 : 
     604                 :                         /* XLOG stuff */
     605               0 :                         if (state->rs_use_wal)
     606               0 :                                 log_newpage(&state->rs_new_rel->rd_node,
     607                 :                                                         state->rs_blockno,
     608                 :                                                         page);
     609                 : 
     610                 :                         /*
     611                 :                          * Now write the page. We say isTemp = true even if it's not a
     612                 :                          * temp table, because there's no need for smgr to schedule an
     613                 :                          * fsync for this write; we'll do it ourselves in
     614                 :                          * end_heap_rewrite.
     615                 :                          */
     616               0 :                         RelationOpenSmgr(state->rs_new_rel);
     617               0 :                         smgrextend(state->rs_new_rel->rd_smgr, state->rs_blockno,
     618                 :                                            (char *) page, true);
     619                 : 
     620               0 :                         state->rs_blockno++;
     621               0 :                         state->rs_buffer_valid = false;
     622                 :                 }
     623                 :         }
     624                 : 
     625              56 :         if (!state->rs_buffer_valid)
     626                 :         {
     627                 :                 /* Initialize a new empty page */
     628               6 :                 PageInit(page, BLCKSZ, 0);
     629               6 :                 state->rs_buffer_valid = true;
     630                 :         }
     631                 : 
     632                 :         /* And now we can insert the tuple into the page */
     633              56 :         newoff = PageAddItem(page, (Item) heaptup->t_data, len,
     634                 :                                                  InvalidOffsetNumber, false, true);
     635              56 :         if (newoff == InvalidOffsetNumber)
     636               0 :                 elog(ERROR, "failed to add tuple");
     637                 : 
     638                 :         /* Update caller's t_self to the actual position where it was stored */
     639              56 :         ItemPointerSet(&(tup->t_self), state->rs_blockno, newoff);
     640                 : 
     641                 :         /*
     642                 :          * Insert the correct position into CTID of the stored tuple, too, if the
     643                 :          * caller didn't supply a valid CTID.
     644                 :          */
     645              56 :         if (!ItemPointerIsValid(&tup->t_data->t_ctid))
     646                 :         {
     647                 :                 ItemId          newitemid;
     648                 :                 HeapTupleHeader onpage_tup;
     649                 : 
     650              51 :                 newitemid = PageGetItemId(page, newoff);
     651              51 :                 onpage_tup = (HeapTupleHeader) PageGetItem(page, newitemid);
     652                 : 
     653              51 :                 onpage_tup->t_ctid = tup->t_self;
     654                 :         }
     655                 : 
     656                 :         /* If heaptup is a private copy, release it. */
     657              56 :         if (heaptup != tup)
     658               1 :                 heap_freetuple(heaptup);
     659              56 : }

Generated by: LTP GCOV extension version 1.5