PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
rewriteheap.h File Reference
#include "access/htup.h"
#include "storage/itemptr.h"
#include "storage/relfilenode.h"
#include "utils/relcache.h"
Include dependency graph for rewriteheap.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  LogicalRewriteMappingData
 

Macros

#define LOGICAL_REWRITE_FORMAT   "map-%x-%x-%X_%X-%x-%x"
 

Typedefs

typedef struct RewriteStateDataRewriteState
 
typedef struct
LogicalRewriteMappingData 
LogicalRewriteMappingData
 

Functions

RewriteState begin_heap_rewrite (Relation OldHeap, Relation NewHeap, TransactionId OldestXmin, TransactionId FreezeXid, MultiXactId MultiXactCutoff, bool use_wal)
 
void end_heap_rewrite (RewriteState state)
 
void rewrite_heap_tuple (RewriteState state, HeapTuple oldTuple, HeapTuple newTuple)
 
bool rewrite_heap_dead_tuple (RewriteState state, HeapTuple oldTuple)
 
void CheckPointLogicalRewriteHeap (void)
 

Macro Definition Documentation

#define LOGICAL_REWRITE_FORMAT   "map-%x-%x-%X_%X-%x-%x"

Typedef Documentation

Definition at line 22 of file rewriteheap.h.

Function Documentation

RewriteState begin_heap_rewrite ( Relation  OldHeap,
Relation  NewHeap,
TransactionId  OldestXmin,
TransactionId  FreezeXid,
MultiXactId  MultiXactCutoff,
bool  use_wal 
)

Definition at line 246 of file rewriteheap.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate(), CurrentMemoryContext, HASHCTL::entrysize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASHCTL::hcxt, HASHCTL::keysize, logical_begin_heap_rewrite(), MemoryContextSwitchTo(), palloc(), palloc0(), RelationGetNumberOfBlocks, RewriteStateData::rs_blockno, RewriteStateData::rs_buffer, RewriteStateData::rs_buffer_valid, RewriteStateData::rs_cutoff_multi, RewriteStateData::rs_cxt, RewriteStateData::rs_freeze_xid, RewriteStateData::rs_new_rel, RewriteStateData::rs_old_new_tid_map, RewriteStateData::rs_old_rel, RewriteStateData::rs_oldest_xmin, RewriteStateData::rs_unresolved_tups, and RewriteStateData::rs_use_wal.

Referenced by copy_heap_data().

249 {
251  MemoryContext rw_cxt;
252  MemoryContext old_cxt;
253  HASHCTL hash_ctl;
254 
255  /*
256  * To ease cleanup, make a separate context that will contain the
257  * RewriteState struct itself plus all subsidiary data.
258  */
260  "Table rewrite",
262  old_cxt = MemoryContextSwitchTo(rw_cxt);
263 
264  /* Create and fill in the state struct */
265  state = palloc0(sizeof(RewriteStateData));
266 
267  state->rs_old_rel = old_heap;
268  state->rs_new_rel = new_heap;
269  state->rs_buffer = (Page) palloc(BLCKSZ);
270  /* new_heap needn't be empty, just locked */
271  state->rs_blockno = RelationGetNumberOfBlocks(new_heap);
272  state->rs_buffer_valid = false;
273  state->rs_use_wal = use_wal;
274  state->rs_oldest_xmin = oldest_xmin;
275  state->rs_freeze_xid = freeze_xid;
276  state->rs_cutoff_multi = cutoff_multi;
277  state->rs_cxt = rw_cxt;
278 
279  /* Initialize hash tables used to track update chains */
280  memset(&hash_ctl, 0, sizeof(hash_ctl));
281  hash_ctl.keysize = sizeof(TidHashKey);
282  hash_ctl.entrysize = sizeof(UnresolvedTupData);
283  hash_ctl.hcxt = state->rs_cxt;
284 
285  state->rs_unresolved_tups =
286  hash_create("Rewrite / Unresolved ctids",
287  128, /* arbitrary initial size */
288  &hash_ctl,
290 
291  hash_ctl.entrysize = sizeof(OldToNewMappingData);
292 
293  state->rs_old_new_tid_map =
294  hash_create("Rewrite / Old to new tid map",
295  128, /* arbitrary initial size */
296  &hash_ctl,
298 
299  MemoryContextSwitchTo(old_cxt);
300 
302 
303  return state;
304 }
#define HASH_CONTEXT
Definition: hsearch.h:93
#define HASH_ELEM
Definition: hsearch.h:87
MemoryContext hcxt
Definition: hsearch.h:78
TransactionId rs_freeze_xid
Definition: rewriteheap.c:150
MultiXactId rs_cutoff_multi
Definition: rewriteheap.c:154
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Size entrysize
Definition: hsearch.h:73
Relation rs_new_rel
Definition: rewriteheap.c:142
HTAB * rs_unresolved_tups
Definition: rewriteheap.c:159
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:145
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
TransactionId rs_oldest_xmin
Definition: rewriteheap.c:148
#define HASH_BLOBS
Definition: hsearch.h:88
MemoryContext rs_cxt
Definition: rewriteheap.c:156
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:440
void * palloc0(Size size)
Definition: mcxt.c:920
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:301
Size keysize
Definition: hsearch.h:72
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:199
static void logical_begin_heap_rewrite(RewriteState state)
Definition: rewriteheap.c:797
HTAB * rs_old_new_tid_map
Definition: rewriteheap.c:160
Definition: regguts.h:298
void * palloc(Size size)
Definition: mcxt.c:891
Relation rs_old_rel
Definition: rewriteheap.c:141
BlockNumber rs_blockno
Definition: rewriteheap.c:144
Pointer Page
Definition: bufpage.h:74
void CheckPointLogicalRewriteHeap ( void  )

Definition at line 1191 of file rewriteheap.c.

References AllocateDir(), CloseTransientFile(), dirent::d_name, DEBUG1, elog, ereport, errcode_for_file_access(), errmsg(), ERROR, fd(), FreeDir(), GetRedoRecPtr(), InvalidXLogRecPtr, LOGICAL_REWRITE_FORMAT, lstat, MAXPGPATH, NULL, OpenTransientFile(), PG_BINARY, pg_fsync(), ReadDir(), ReplicationSlotsComputeLogicalRestartLSN(), snprintf(), and unlink().

Referenced by CheckPointGuts().

1192 {
1193  XLogRecPtr cutoff;
1194  XLogRecPtr redo;
1195  DIR *mappings_dir;
1196  struct dirent *mapping_de;
1197  char path[MAXPGPATH];
1198 
1199  /*
1200  * We start of with a minimum of the last redo pointer. No new decoding
1201  * slot will start before that, so that's a safe upper bound for removal.
1202  */
1203  redo = GetRedoRecPtr();
1204 
1205  /* now check for the restart ptrs from existing slots */
1207 
1208  /* don't start earlier than the restart lsn */
1209  if (cutoff != InvalidXLogRecPtr && redo < cutoff)
1210  cutoff = redo;
1211 
1212  mappings_dir = AllocateDir("pg_logical/mappings");
1213  while ((mapping_de = ReadDir(mappings_dir, "pg_logical/mappings")) != NULL)
1214  {
1215  struct stat statbuf;
1216  Oid dboid;
1217  Oid relid;
1218  XLogRecPtr lsn;
1219  TransactionId rewrite_xid;
1220  TransactionId create_xid;
1221  uint32 hi,
1222  lo;
1223 
1224  if (strcmp(mapping_de->d_name, ".") == 0 ||
1225  strcmp(mapping_de->d_name, "..") == 0)
1226  continue;
1227 
1228  snprintf(path, MAXPGPATH, "pg_logical/mappings/%s", mapping_de->d_name);
1229  if (lstat(path, &statbuf) == 0 && !S_ISREG(statbuf.st_mode))
1230  continue;
1231 
1232  /* Skip over files that cannot be ours. */
1233  if (strncmp(mapping_de->d_name, "map-", 4) != 0)
1234  continue;
1235 
1236  if (sscanf(mapping_de->d_name, LOGICAL_REWRITE_FORMAT,
1237  &dboid, &relid, &hi, &lo, &rewrite_xid, &create_xid) != 6)
1238  elog(ERROR, "could not parse filename \"%s\"", mapping_de->d_name);
1239 
1240  lsn = ((uint64) hi) << 32 | lo;
1241 
1242  if (lsn < cutoff || cutoff == InvalidXLogRecPtr)
1243  {
1244  elog(DEBUG1, "removing logical rewrite file \"%s\"", path);
1245  if (unlink(path) < 0)
1246  ereport(ERROR,
1248  errmsg("could not remove file \"%s\": %m", path)));
1249  }
1250  else
1251  {
1252  int fd = OpenTransientFile(path, O_RDONLY | PG_BINARY, 0);
1253 
1254  /*
1255  * The file cannot vanish due to concurrency since this function
1256  * is the only one removing logical mappings and it's run while
1257  * CheckpointLock is held exclusively.
1258  */
1259  if (fd < 0)
1260  ereport(ERROR,
1262  errmsg("could not open file \"%s\": %m", path)));
1263 
1264  /*
1265  * We could try to avoid fsyncing files that either haven't
1266  * changed or have only been created since the checkpoint's start,
1267  * but it's currently not deemed worth the effort.
1268  */
1269  else if (pg_fsync(fd) != 0)
1270  ereport(ERROR,
1272  errmsg("could not fsync file \"%s\": %m", path)));
1273  CloseTransientFile(fd);
1274  }
1275  }
1276  FreeDir(mappings_dir);
1277 }
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
#define DEBUG1
Definition: elog.h:25
uint32 TransactionId
Definition: c.h:393
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
unsigned int Oid
Definition: postgres_ext.h:31
Definition: dirent.h:9
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1037
Definition: dirent.c:25
#define ERROR
Definition: elog.h:43
#define MAXPGPATH
int OpenTransientFile(FileName fileName, int fileFlags, int fileMode)
Definition: fd.c:2093
int errcode_for_file_access(void)
Definition: elog.c:598
XLogRecPtr ReplicationSlotsComputeLogicalRestartLSN(void)
Definition: slot.c:707
unsigned int uint32
Definition: c.h:265
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2284
int unlink(const char *filename)
#define ereport(elevel, rest)
Definition: elog.h:122
int CloseTransientFile(int fd)
Definition: fd.c:2254
#define NULL
Definition: c.h:226
uint64 XLogRecPtr
Definition: xlogdefs.h:21
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2350
XLogRecPtr GetRedoRecPtr(void)
Definition: xlog.c:8115
#define LOGICAL_REWRITE_FORMAT
Definition: rewriteheap.h:54
int errmsg(const char *fmt,...)
Definition: elog.c:797
int pg_fsync(int fd)
Definition: fd.c:333
char d_name[MAX_PATH]
Definition: dirent.h:14
#define elog
Definition: elog.h:219
#define lstat(path, sb)
Definition: win32.h:272
int FreeDir(DIR *dir)
Definition: fd.c:2393
void end_heap_rewrite ( RewriteState  state)

Definition at line 312 of file rewriteheap.c.

References hash_seq_init(), hash_seq_search(), heap_sync(), ItemPointerSetInvalid, log_newpage(), logical_end_heap_rewrite(), MAIN_FORKNUM, MemoryContextDelete(), NULL, PageSetChecksumInplace(), raw_heap_insert(), RelationData::rd_node, RelationData::rd_smgr, RelationNeedsWAL, RelationOpenSmgr, RewriteStateData::rs_blockno, RewriteStateData::rs_buffer, RewriteStateData::rs_buffer_valid, RewriteStateData::rs_cxt, RewriteStateData::rs_new_rel, RewriteStateData::rs_unresolved_tups, RewriteStateData::rs_use_wal, smgrextend(), HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, and UnresolvedTupData::tuple.

Referenced by copy_heap_data().

313 {
314  HASH_SEQ_STATUS seq_status;
315  UnresolvedTup unresolved;
316 
317  /*
318  * Write any remaining tuples in the UnresolvedTups table. If we have any
319  * left, they should in fact be dead, but let's err on the safe side.
320  */
321  hash_seq_init(&seq_status, state->rs_unresolved_tups);
322 
323  while ((unresolved = hash_seq_search(&seq_status)) != NULL)
324  {
325  ItemPointerSetInvalid(&unresolved->tuple->t_data->t_ctid);
326  raw_heap_insert(state, unresolved->tuple);
327  }
328 
329  /* Write the last page, if any */
330  if (state->rs_buffer_valid)
331  {
332  if (state->rs_use_wal)
333  log_newpage(&state->rs_new_rel->rd_node,
334  MAIN_FORKNUM,
335  state->rs_blockno,
336  state->rs_buffer,
337  true);
339 
341 
343  (char *) state->rs_buffer, true);
344  }
345 
346  /*
347  * If the rel is WAL-logged, must fsync before commit. We use heap_sync
348  * to ensure that the toast table gets fsync'd too.
349  *
350  * It's obvious that we must do this when not WAL-logging. It's less
351  * obvious that we have to do it even if we did WAL-log the pages. The
352  * reason is the same as in tablecmds.c's copy_relation_data(): we're
353  * writing data that's not in shared buffers, and so a CHECKPOINT
354  * occurring during the rewriteheap operation won't have fsync'd data we
355  * wrote before the checkpoint.
356  */
357  if (RelationNeedsWAL(state->rs_new_rel))
358  heap_sync(state->rs_new_rel);
359 
361 
362  /* Deleting the context frees everything */
363  MemoryContextDelete(state->rs_cxt);
364 }
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:200
static void logical_end_heap_rewrite(RewriteState state)
Definition: rewriteheap.c:943
struct SMgrRelationData * rd_smgr
Definition: rel.h:87
Relation rs_new_rel
Definition: rewriteheap.c:142
void heap_sync(Relation rel)
Definition: heapam.c:9122
HeapTupleHeader t_data
Definition: htup.h:67
#define RelationOpenSmgr(relation)
Definition: rel.h:457
ItemPointerData t_ctid
Definition: htup_details.h:150
HTAB * rs_unresolved_tups
Definition: rewriteheap.c:159
MemoryContext rs_cxt
Definition: rewriteheap.c:156
RelFileNode rd_node
Definition: rel.h:85
#define NULL
Definition: c.h:226
void PageSetChecksumInplace(Page page, BlockNumber blkno)
Definition: bufpage.c:1172
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1353
#define RelationNeedsWAL(relation)
Definition: rel.h:502
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1343
void smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer, bool skipFsync)
Definition: smgr.c:600
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:131
XLogRecPtr log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno, Page page, bool page_std)
Definition: xloginsert.c:973
static void raw_heap_insert(RewriteState state, HeapTuple tup)
Definition: rewriteheap.c:626
BlockNumber rs_blockno
Definition: rewriteheap.c:144
bool rewrite_heap_dead_tuple ( RewriteState  state,
HeapTuple  oldTuple 
)

Definition at line 576 of file rewriteheap.c.

References Assert, HASH_FIND, HASH_REMOVE, hash_search(), heap_freetuple(), HeapTupleHeaderGetXmin, NULL, RewriteStateData::rs_unresolved_tups, HeapTupleData::t_data, HeapTupleData::t_self, TidHashKey::tid, UnresolvedTupData::tuple, and TidHashKey::xmin.

Referenced by copy_heap_data().

577 {
578  /*
579  * If we have already seen an earlier tuple in the update chain that
580  * points to this tuple, let's forget about that earlier tuple. It's in
581  * fact dead as well, our simple xmax < OldestXmin test in
582  * HeapTupleSatisfiesVacuum just wasn't enough to detect it. It happens
583  * when xmin of a tuple is greater than xmax, which sounds
584  * counter-intuitive but is perfectly valid.
585  *
586  * We don't bother to try to detect the situation the other way round,
587  * when we encounter the dead tuple first and then the recently dead one
588  * that points to it. If that happens, we'll have some unmatched entries
589  * in the UnresolvedTups hash table at the end. That can happen anyway,
590  * because a vacuum might have removed the dead tuple in the chain before
591  * us.
592  */
593  UnresolvedTup unresolved;
594  TidHashKey hashkey;
595  bool found;
596 
597  memset(&hashkey, 0, sizeof(hashkey));
598  hashkey.xmin = HeapTupleHeaderGetXmin(old_tuple->t_data);
599  hashkey.tid = old_tuple->t_self;
600 
601  unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
602  HASH_FIND, NULL);
603 
604  if (unresolved != NULL)
605  {
606  /* Need to free the contained tuple as well as the hashtable entry */
607  heap_freetuple(unresolved->tuple);
608  hash_search(state->rs_unresolved_tups, &hashkey,
609  HASH_REMOVE, &found);
610  Assert(found);
611  return true;
612  }
613 
614  return false;
615 }
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:885
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1374
HTAB * rs_unresolved_tups
Definition: rewriteheap.c:159
ItemPointerData tid
Definition: rewriteheap.c:174
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:670
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:307
TransactionId xmin
Definition: rewriteheap.c:173
void rewrite_heap_tuple ( RewriteState  state,
HeapTuple  oldTuple,
HeapTuple  newTuple 
)

Definition at line 378 of file rewriteheap.c.

References Assert, HASH_ENTER, HASH_FIND, HASH_REMOVE, hash_search(), HEAP2_XACT_MASK, heap_copytuple(), heap_freetuple(), heap_freeze_tuple(), HEAP_UPDATED, HEAP_XACT_MASK, HEAP_XMAX_INVALID, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleHeaderIsOnlyLocked(), ItemPointerEquals(), ItemPointerSetInvalid, logical_rewrite_heap_tuple(), MemoryContextSwitchTo(), OldToNewMappingData::new_tid, NULL, UnresolvedTupData::old_tid, raw_heap_insert(), RewriteStateData::rs_cutoff_multi, RewriteStateData::rs_cxt, RewriteStateData::rs_freeze_xid, RewriteStateData::rs_old_new_tid_map, RewriteStateData::rs_oldest_xmin, RewriteStateData::rs_unresolved_tups, HeapTupleHeaderData::t_choice, HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleHeaderData::t_heap, HeapTupleHeaderData::t_infomask, HeapTupleHeaderData::t_infomask2, HeapTupleData::t_self, TidHashKey::tid, TransactionIdPrecedes(), UnresolvedTupData::tuple, and TidHashKey::xmin.

Referenced by reform_and_rewrite_tuple().

380 {
381  MemoryContext old_cxt;
382  ItemPointerData old_tid;
383  TidHashKey hashkey;
384  bool found;
385  bool free_new;
386 
387  old_cxt = MemoryContextSwitchTo(state->rs_cxt);
388 
389  /*
390  * Copy the original tuple's visibility information into new_tuple.
391  *
392  * XXX we might later need to copy some t_infomask2 bits, too? Right now,
393  * we intentionally clear the HOT status bits.
394  */
395  memcpy(&new_tuple->t_data->t_choice.t_heap,
396  &old_tuple->t_data->t_choice.t_heap,
397  sizeof(HeapTupleFields));
398 
399  new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
400  new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
401  new_tuple->t_data->t_infomask |=
402  old_tuple->t_data->t_infomask & HEAP_XACT_MASK;
403 
404  /*
405  * While we have our hands on the tuple, we may as well freeze any
406  * eligible xmin or xmax, so that future VACUUM effort can be saved.
407  */
408  heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid,
409  state->rs_cutoff_multi);
410 
411  /*
412  * Invalid ctid means that ctid should point to the tuple itself. We'll
413  * override it later if the tuple is part of an update chain.
414  */
415  ItemPointerSetInvalid(&new_tuple->t_data->t_ctid);
416 
417  /*
418  * If the tuple has been updated, check the old-to-new mapping hash table.
419  */
420  if (!((old_tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
421  HeapTupleHeaderIsOnlyLocked(old_tuple->t_data)) &&
422  !(ItemPointerEquals(&(old_tuple->t_self),
423  &(old_tuple->t_data->t_ctid))))
424  {
425  OldToNewMapping mapping;
426 
427  memset(&hashkey, 0, sizeof(hashkey));
428  hashkey.xmin = HeapTupleHeaderGetUpdateXid(old_tuple->t_data);
429  hashkey.tid = old_tuple->t_data->t_ctid;
430 
431  mapping = (OldToNewMapping)
432  hash_search(state->rs_old_new_tid_map, &hashkey,
433  HASH_FIND, NULL);
434 
435  if (mapping != NULL)
436  {
437  /*
438  * We've already copied the tuple that t_ctid points to, so we can
439  * set the ctid of this tuple to point to the new location, and
440  * insert it right away.
441  */
442  new_tuple->t_data->t_ctid = mapping->new_tid;
443 
444  /* We don't need the mapping entry anymore */
445  hash_search(state->rs_old_new_tid_map, &hashkey,
446  HASH_REMOVE, &found);
447  Assert(found);
448  }
449  else
450  {
451  /*
452  * We haven't seen the tuple t_ctid points to yet. Stash this
453  * tuple into unresolved_tups to be written later.
454  */
455  UnresolvedTup unresolved;
456 
457  unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
458  HASH_ENTER, &found);
459  Assert(!found);
460 
461  unresolved->old_tid = old_tuple->t_self;
462  unresolved->tuple = heap_copytuple(new_tuple);
463 
464  /*
465  * We can't do anything more now, since we don't know where the
466  * tuple will be written.
467  */
468  MemoryContextSwitchTo(old_cxt);
469  return;
470  }
471  }
472 
473  /*
474  * Now we will write the tuple, and then check to see if it is the B tuple
475  * in any new or known pair. When we resolve a known pair, we will be
476  * able to write that pair's A tuple, and then we have to check if it
477  * resolves some other pair. Hence, we need a loop here.
478  */
479  old_tid = old_tuple->t_self;
480  free_new = false;
481 
482  for (;;)
483  {
484  ItemPointerData new_tid;
485 
486  /* Insert the tuple and find out where it's put in new_heap */
487  raw_heap_insert(state, new_tuple);
488  new_tid = new_tuple->t_self;
489 
490  logical_rewrite_heap_tuple(state, old_tid, new_tuple);
491 
492  /*
493  * If the tuple is the updated version of a row, and the prior version
494  * wouldn't be DEAD yet, then we need to either resolve the prior
495  * version (if it's waiting in rs_unresolved_tups), or make an entry
496  * in rs_old_new_tid_map (so we can resolve it when we do see it). The
497  * previous tuple's xmax would equal this one's xmin, so it's
498  * RECENTLY_DEAD if and only if the xmin is not before OldestXmin.
499  */
500  if ((new_tuple->t_data->t_infomask & HEAP_UPDATED) &&
501  !TransactionIdPrecedes(HeapTupleHeaderGetXmin(new_tuple->t_data),
502  state->rs_oldest_xmin))
503  {
504  /*
505  * Okay, this is B in an update pair. See if we've seen A.
506  */
507  UnresolvedTup unresolved;
508 
509  memset(&hashkey, 0, sizeof(hashkey));
510  hashkey.xmin = HeapTupleHeaderGetXmin(new_tuple->t_data);
511  hashkey.tid = old_tid;
512 
513  unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
514  HASH_FIND, NULL);
515 
516  if (unresolved != NULL)
517  {
518  /*
519  * We have seen and memorized the previous tuple already. Now
520  * that we know where we inserted the tuple its t_ctid points
521  * to, fix its t_ctid and insert it to the new heap.
522  */
523  if (free_new)
524  heap_freetuple(new_tuple);
525  new_tuple = unresolved->tuple;
526  free_new = true;
527  old_tid = unresolved->old_tid;
528  new_tuple->t_data->t_ctid = new_tid;
529 
530  /*
531  * We don't need the hash entry anymore, but don't free its
532  * tuple just yet.
533  */
534  hash_search(state->rs_unresolved_tups, &hashkey,
535  HASH_REMOVE, &found);
536  Assert(found);
537 
538  /* loop back to insert the previous tuple in the chain */
539  continue;
540  }
541  else
542  {
543  /*
544  * Remember the new tid of this tuple. We'll use it to set the
545  * ctid when we find the previous tuple in the chain.
546  */
547  OldToNewMapping mapping;
548 
549  mapping = hash_search(state->rs_old_new_tid_map, &hashkey,
550  HASH_ENTER, &found);
551  Assert(!found);
552 
553  mapping->new_tid = new_tid;
554  }
555  }
556 
557  /* Done with this (chain of) tuples, for now */
558  if (free_new)
559  heap_freetuple(new_tuple);
560  break;
561  }
562 
563  MemoryContextSwitchTo(old_cxt);
564 }
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:359
bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
Definition: tqual.c:1585
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:608
static void logical_rewrite_heap_tuple(RewriteState state, ItemPointerData old_tid, HeapTuple new_tuple)
Definition: rewriteheap.c:1041
TransactionId rs_freeze_xid
Definition: rewriteheap.c:150
MultiXactId rs_cutoff_multi
Definition: rewriteheap.c:154
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define HEAP2_XACT_MASK
Definition: htup_details.h:269
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:885
#define HEAP_UPDATED
Definition: htup_details.h:195
ItemPointerData old_tid
Definition: rewriteheap.c:183
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1374
bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid, TransactionId cutoff_multi)
Definition: heapam.c:6807
ItemPointerData new_tid
Definition: rewriteheap.c:192
#define HEAP_XMAX_INVALID
Definition: htup_details.h:193
HTAB * rs_unresolved_tups
Definition: rewriteheap.c:159
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
TransactionId rs_oldest_xmin
Definition: rewriteheap.c:148
MemoryContext rs_cxt
Definition: rewriteheap.c:156
ItemPointerData tid
Definition: rewriteheap.c:174
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:670
HTAB * rs_old_new_tid_map
Definition: rewriteheap.c:160
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:307
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:131
TransactionId xmin
Definition: rewriteheap.c:173
static void raw_heap_insert(RewriteState state, HeapTuple tup)
Definition: rewriteheap.c:626
#define HEAP_XACT_MASK
Definition: htup_details.h:204
OldToNewMappingData * OldToNewMapping
Definition: rewriteheap.c:195