PostgreSQL Source Code  git master
heapam_handler.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heaptoast.h"
#include "access/multixact.h"
#include "access/rewriteheap.h"
#include "access/syncscan.h"
#include "access/tableam.h"
#include "access/tsmapi.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "commands/progress.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/bufpage.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/rel.h"
Include dependency graph for heapam_handler.c:

Go to the source code of this file.

Macros

#define HEAP_OVERHEAD_BYTES_PER_TUPLE    (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
 
#define HEAP_USABLE_BYTES_PER_PAGE    (BLCKSZ - SizeOfPageHeaderData)
 

Functions

static TM_Result heapam_tuple_lock_internal (Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd, bool updated)
 
static void reform_and_rewrite_tuple (HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull, RewriteState rwstate)
 
static bool SampleHeapTupleVisible (TableScanDesc scan, Buffer buffer, HeapTuple tuple, OffsetNumber tupoffset)
 
static BlockNumber heapam_scan_get_blocks_done (HeapScanDesc hscan)
 
static const TupleTableSlotOpsheapam_slot_callbacks (Relation relation)
 
static IndexFetchTableDataheapam_index_fetch_begin (Relation rel)
 
static void heapam_index_fetch_reset (IndexFetchTableData *scan)
 
static void heapam_index_fetch_end (IndexFetchTableData *scan)
 
static bool heapam_index_fetch_tuple (struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *call_again, bool *all_dead)
 
static bool heapam_fetch_row_version (Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
 
static bool heapam_tuple_tid_valid (TableScanDesc scan, ItemPointer tid)
 
static bool heapam_tuple_satisfies_snapshot (Relation rel, TupleTableSlot *slot, Snapshot snapshot)
 
static void heapam_tuple_insert (Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate)
 
static void heapam_tuple_insert_speculative (Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate, uint32 specToken)
 
static void heapam_tuple_complete_speculative (Relation relation, TupleTableSlot *slot, uint32 specToken, bool succeeded)
 
static TM_Result heapam_tuple_delete (Relation relation, ItemPointer tid, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart, LazyTupleTableSlot *lockedSlot)
 
static TM_Result heapam_tuple_update (Relation relation, ItemPointer otid, TupleTableSlot *slot, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes, LazyTupleTableSlot *lockedSlot)
 
static TM_Result heapam_tuple_lock (Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
 
static void heapam_relation_set_new_filelocator (Relation rel, const RelFileLocator *newrlocator, char persistence, TransactionId *freezeXid, MultiXactId *minmulti)
 
static void heapam_relation_nontransactional_truncate (Relation rel)
 
static void heapam_relation_copy_data (Relation rel, const RelFileLocator *newrlocator)
 
static void heapam_relation_copy_for_cluster (Relation OldHeap, Relation NewHeap, Relation OldIndex, bool use_sort, TransactionId OldestXmin, TransactionId *xid_cutoff, MultiXactId *multi_cutoff, double *num_tuples, double *tups_vacuumed, double *tups_recently_dead)
 
static bool heapam_scan_analyze_next_block (TableScanDesc scan, BlockNumber blockno, BufferAccessStrategy bstrategy)
 
static bool heapam_scan_analyze_next_tuple (TableScanDesc scan, TransactionId OldestXmin, double *liverows, double *deadrows, TupleTableSlot *slot)
 
static double heapam_index_build_range_scan (Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, bool allow_sync, bool anyvisible, bool progress, BlockNumber start_blockno, BlockNumber numblocks, IndexBuildCallback callback, void *callback_state, TableScanDesc scan)
 
static void heapam_index_validate_scan (Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, Snapshot snapshot, ValidateIndexState *state)
 
static bool heapam_relation_needs_toast_table (Relation rel)
 
static Oid heapam_relation_toast_am (Relation rel)
 
static void heapam_estimate_rel_size (Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac)
 
static bool heapam_scan_bitmap_next_block (TableScanDesc scan, TBMIterateResult *tbmres)
 
static bool heapam_scan_bitmap_next_tuple (TableScanDesc scan, TBMIterateResult *tbmres, TupleTableSlot *slot)
 
static bool heapam_scan_sample_next_block (TableScanDesc scan, SampleScanState *scanstate)
 
static bool heapam_scan_sample_next_tuple (TableScanDesc scan, SampleScanState *scanstate, TupleTableSlot *slot)
 
const TableAmRoutineGetHeapamTableAmRoutine (void)
 
Datum heap_tableam_handler (PG_FUNCTION_ARGS)
 

Variables

static const TableAmRoutine heapam_methods
 

Macro Definition Documentation

◆ HEAP_OVERHEAD_BYTES_PER_TUPLE

#define HEAP_OVERHEAD_BYTES_PER_TUPLE    (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))

Definition at line 2187 of file heapam_handler.c.

◆ HEAP_USABLE_BYTES_PER_PAGE

#define HEAP_USABLE_BYTES_PER_PAGE    (BLCKSZ - SizeOfPageHeaderData)

Definition at line 2189 of file heapam_handler.c.

Function Documentation

◆ GetHeapamTableAmRoutine()

const TableAmRoutine* GetHeapamTableAmRoutine ( void  )

Definition at line 2695 of file heapam_handler.c.

2696 {
2697  return &heapam_methods;
2698 }
static const TableAmRoutine heapam_methods

References heapam_methods.

Referenced by formrdesc(), and heap_getnext().

◆ heap_tableam_handler()

Datum heap_tableam_handler ( PG_FUNCTION_ARGS  )

Definition at line 2701 of file heapam_handler.c.

2702 {
2704 }
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361

References heapam_methods, and PG_RETURN_POINTER.

◆ heapam_estimate_rel_size()

static void heapam_estimate_rel_size ( Relation  rel,
int32 attr_widths,
BlockNumber pages,
double *  tuples,
double *  allvisfrac 
)
static

Definition at line 2193 of file heapam_handler.c.

2196 {
2197  table_block_relation_estimate_size(rel, attr_widths, pages,
2198  tuples, allvisfrac,
2201 }
#define HEAP_OVERHEAD_BYTES_PER_TUPLE
#define HEAP_USABLE_BYTES_PER_PAGE
void table_block_relation_estimate_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac, Size overhead_bytes_per_tuple, Size usable_bytes_per_page)
Definition: tableam.c:665

References HEAP_OVERHEAD_BYTES_PER_TUPLE, HEAP_USABLE_BYTES_PER_PAGE, and table_block_relation_estimate_size().

◆ heapam_fetch_row_version()

static bool heapam_fetch_row_version ( Relation  relation,
ItemPointer  tid,
Snapshot  snapshot,
TupleTableSlot slot 
)
static

Definition at line 186 of file heapam_handler.c.

190 {
192  Buffer buffer;
193 
194  Assert(TTS_IS_BUFFERTUPLE(slot));
195 
196  bslot->base.tupdata.t_self = *tid;
197  if (heap_fetch(relation, snapshot, &bslot->base.tupdata, &buffer, false))
198  {
199  /* store in slot, transferring existing pin */
200  ExecStorePinnedBufferHeapTuple(&bslot->base.tupdata, slot, buffer);
201  slot->tts_tableOid = RelationGetRelid(relation);
202 
203  return true;
204  }
205 
206  return false;
207 }
int Buffer
Definition: buf.h:23
TupleTableSlot * ExecStorePinnedBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
Definition: execTuples.c:1419
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf)
Definition: heapam.c:1352
Assert(fmt[strlen(fmt) - 1] !='\n')
#define RelationGetRelid(relation)
Definition: rel.h:503
Oid tts_tableOid
Definition: tuptable.h:131
#define TTS_IS_BUFFERTUPLE(slot)
Definition: tuptable.h:231

References Assert(), ExecStorePinnedBufferHeapTuple(), heap_fetch(), RelationGetRelid, TTS_IS_BUFFERTUPLE, and TupleTableSlot::tts_tableOid.

◆ heapam_index_build_range_scan()

static double heapam_index_build_range_scan ( Relation  heapRelation,
Relation  indexRelation,
IndexInfo indexInfo,
bool  allow_sync,
bool  anyvisible,
bool  progress,
BlockNumber  start_blockno,
BlockNumber  numblocks,
IndexBuildCallback  callback,
void *  callback_state,
TableScanDesc  scan 
)
static

Definition at line 1261 of file heapam_handler.c.

1272 {
1273  HeapScanDesc hscan;
1274  bool is_system_catalog;
1275  bool checking_uniqueness;
1276  HeapTuple heapTuple;
1278  bool isnull[INDEX_MAX_KEYS];
1279  double reltuples;
1280  ExprState *predicate;
1281  TupleTableSlot *slot;
1282  EState *estate;
1283  ExprContext *econtext;
1284  Snapshot snapshot;
1285  bool need_unregister_snapshot = false;
1286  TransactionId OldestXmin;
1287  BlockNumber previous_blkno = InvalidBlockNumber;
1288  BlockNumber root_blkno = InvalidBlockNumber;
1289  OffsetNumber root_offsets[MaxHeapTuplesPerPage];
1290 
1291  /*
1292  * sanity checks
1293  */
1294  Assert(OidIsValid(indexRelation->rd_rel->relam));
1295 
1296  /* Remember if it's a system catalog */
1297  is_system_catalog = IsSystemRelation(heapRelation);
1298 
1299  /* See whether we're verifying uniqueness/exclusion properties */
1300  checking_uniqueness = (indexInfo->ii_Unique ||
1301  indexInfo->ii_ExclusionOps != NULL);
1302 
1303  /*
1304  * "Any visible" mode is not compatible with uniqueness checks; make sure
1305  * only one of those is requested.
1306  */
1307  Assert(!(anyvisible && checking_uniqueness));
1308 
1309  /*
1310  * Need an EState for evaluation of index expressions and partial-index
1311  * predicates. Also a slot to hold the current tuple.
1312  */
1313  estate = CreateExecutorState();
1314  econtext = GetPerTupleExprContext(estate);
1315  slot = table_slot_create(heapRelation, NULL);
1316 
1317  /* Arrange for econtext's scan tuple to be the tuple under test */
1318  econtext->ecxt_scantuple = slot;
1319 
1320  /* Set up execution state for predicate, if any. */
1321  predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1322 
1323  /*
1324  * Prepare for scan of the base relation. In a normal index build, we use
1325  * SnapshotAny because we must retrieve all tuples and do our own time
1326  * qual checks (because we have to index RECENTLY_DEAD tuples). In a
1327  * concurrent build, or during bootstrap, we take a regular MVCC snapshot
1328  * and index whatever's live according to that.
1329  */
1330  OldestXmin = InvalidTransactionId;
1331 
1332  /* okay to ignore lazy VACUUMs here */
1333  if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)
1334  OldestXmin = GetOldestNonRemovableTransactionId(heapRelation);
1335 
1336  if (!scan)
1337  {
1338  /*
1339  * Serial index build.
1340  *
1341  * Must begin our own heap scan in this case. We may also need to
1342  * register a snapshot whose lifetime is under our direct control.
1343  */
1344  if (!TransactionIdIsValid(OldestXmin))
1345  {
1347  need_unregister_snapshot = true;
1348  }
1349  else
1350  snapshot = SnapshotAny;
1351 
1352  scan = table_beginscan_strat(heapRelation, /* relation */
1353  snapshot, /* snapshot */
1354  0, /* number of keys */
1355  NULL, /* scan key */
1356  true, /* buffer access strategy OK */
1357  allow_sync); /* syncscan OK? */
1358  }
1359  else
1360  {
1361  /*
1362  * Parallel index build.
1363  *
1364  * Parallel case never registers/unregisters own snapshot. Snapshot
1365  * is taken from parallel heap scan, and is SnapshotAny or an MVCC
1366  * snapshot, based on same criteria as serial case.
1367  */
1369  Assert(allow_sync);
1370  snapshot = scan->rs_snapshot;
1371  }
1372 
1373  hscan = (HeapScanDesc) scan;
1374 
1375  /*
1376  * Must have called GetOldestNonRemovableTransactionId() if using
1377  * SnapshotAny. Shouldn't have for an MVCC snapshot. (It's especially
1378  * worth checking this for parallel builds, since ambuild routines that
1379  * support parallel builds must work these details out for themselves.)
1380  */
1381  Assert(snapshot == SnapshotAny || IsMVCCSnapshot(snapshot));
1382  Assert(snapshot == SnapshotAny ? TransactionIdIsValid(OldestXmin) :
1383  !TransactionIdIsValid(OldestXmin));
1384  Assert(snapshot == SnapshotAny || !anyvisible);
1385 
1386  /* Publish number of blocks to scan */
1387  if (progress)
1388  {
1389  BlockNumber nblocks;
1390 
1391  if (hscan->rs_base.rs_parallel != NULL)
1392  {
1394 
1396  nblocks = pbscan->phs_nblocks;
1397  }
1398  else
1399  nblocks = hscan->rs_nblocks;
1400 
1402  nblocks);
1403  }
1404 
1405  /* set our scan endpoints */
1406  if (!allow_sync)
1407  heap_setscanlimits(scan, start_blockno, numblocks);
1408  else
1409  {
1410  /* syncscan can only be requested on whole relation */
1411  Assert(start_blockno == 0);
1412  Assert(numblocks == InvalidBlockNumber);
1413  }
1414 
1415  reltuples = 0;
1416 
1417  /*
1418  * Scan all tuples in the base relation.
1419  */
1420  while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1421  {
1422  bool tupleIsAlive;
1423 
1425 
1426  /* Report scan progress, if asked to. */
1427  if (progress)
1428  {
1429  BlockNumber blocks_done = heapam_scan_get_blocks_done(hscan);
1430 
1431  if (blocks_done != previous_blkno)
1432  {
1434  blocks_done);
1435  previous_blkno = blocks_done;
1436  }
1437  }
1438 
1439  /*
1440  * When dealing with a HOT-chain of updated tuples, we want to index
1441  * the values of the live tuple (if any), but index it under the TID
1442  * of the chain's root tuple. This approach is necessary to preserve
1443  * the HOT-chain structure in the heap. So we need to be able to find
1444  * the root item offset for every tuple that's in a HOT-chain. When
1445  * first reaching a new page of the relation, call
1446  * heap_get_root_tuples() to build a map of root item offsets on the
1447  * page.
1448  *
1449  * It might look unsafe to use this information across buffer
1450  * lock/unlock. However, we hold ShareLock on the table so no
1451  * ordinary insert/update/delete should occur; and we hold pin on the
1452  * buffer continuously while visiting the page, so no pruning
1453  * operation can occur either.
1454  *
1455  * In cases with only ShareUpdateExclusiveLock on the table, it's
1456  * possible for some HOT tuples to appear that we didn't know about
1457  * when we first read the page. To handle that case, we re-obtain the
1458  * list of root offsets when a HOT tuple points to a root item that we
1459  * don't know about.
1460  *
1461  * Also, although our opinions about tuple liveness could change while
1462  * we scan the page (due to concurrent transaction commits/aborts),
1463  * the chain root locations won't, so this info doesn't need to be
1464  * rebuilt after waiting for another transaction.
1465  *
1466  * Note the implied assumption that there is no more than one live
1467  * tuple per HOT-chain --- else we could create more than one index
1468  * entry pointing to the same root tuple.
1469  */
1470  if (hscan->rs_cblock != root_blkno)
1471  {
1472  Page page = BufferGetPage(hscan->rs_cbuf);
1473 
1475  heap_get_root_tuples(page, root_offsets);
1477 
1478  root_blkno = hscan->rs_cblock;
1479  }
1480 
1481  if (snapshot == SnapshotAny)
1482  {
1483  /* do our own time qual check */
1484  bool indexIt;
1485  TransactionId xwait;
1486 
1487  recheck:
1488 
1489  /*
1490  * We could possibly get away with not locking the buffer here,
1491  * since caller should hold ShareLock on the relation, but let's
1492  * be conservative about it. (This remark is still correct even
1493  * with HOT-pruning: our pin on the buffer prevents pruning.)
1494  */
1496 
1497  /*
1498  * The criteria for counting a tuple as live in this block need to
1499  * match what analyze.c's heapam_scan_analyze_next_tuple() does,
1500  * otherwise CREATE INDEX and ANALYZE may produce wildly different
1501  * reltuples values, e.g. when there are many recently-dead
1502  * tuples.
1503  */
1504  switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
1505  hscan->rs_cbuf))
1506  {
1507  case HEAPTUPLE_DEAD:
1508  /* Definitely dead, we can ignore it */
1509  indexIt = false;
1510  tupleIsAlive = false;
1511  break;
1512  case HEAPTUPLE_LIVE:
1513  /* Normal case, index and unique-check it */
1514  indexIt = true;
1515  tupleIsAlive = true;
1516  /* Count it as live, too */
1517  reltuples += 1;
1518  break;
1520 
1521  /*
1522  * If tuple is recently deleted then we must index it
1523  * anyway to preserve MVCC semantics. (Pre-existing
1524  * transactions could try to use the index after we finish
1525  * building it, and may need to see such tuples.)
1526  *
1527  * However, if it was HOT-updated then we must only index
1528  * the live tuple at the end of the HOT-chain. Since this
1529  * breaks semantics for pre-existing snapshots, mark the
1530  * index as unusable for them.
1531  *
1532  * We don't count recently-dead tuples in reltuples, even
1533  * if we index them; see heapam_scan_analyze_next_tuple().
1534  */
1535  if (HeapTupleIsHotUpdated(heapTuple))
1536  {
1537  indexIt = false;
1538  /* mark the index as unsafe for old snapshots */
1539  indexInfo->ii_BrokenHotChain = true;
1540  }
1541  else
1542  indexIt = true;
1543  /* In any case, exclude the tuple from unique-checking */
1544  tupleIsAlive = false;
1545  break;
1547 
1548  /*
1549  * In "anyvisible" mode, this tuple is visible and we
1550  * don't need any further checks.
1551  */
1552  if (anyvisible)
1553  {
1554  indexIt = true;
1555  tupleIsAlive = true;
1556  reltuples += 1;
1557  break;
1558  }
1559 
1560  /*
1561  * Since caller should hold ShareLock or better, normally
1562  * the only way to see this is if it was inserted earlier
1563  * in our own transaction. However, it can happen in
1564  * system catalogs, since we tend to release write lock
1565  * before commit there. Give a warning if neither case
1566  * applies.
1567  */
1568  xwait = HeapTupleHeaderGetXmin(heapTuple->t_data);
1570  {
1571  if (!is_system_catalog)
1572  elog(WARNING, "concurrent insert in progress within table \"%s\"",
1573  RelationGetRelationName(heapRelation));
1574 
1575  /*
1576  * If we are performing uniqueness checks, indexing
1577  * such a tuple could lead to a bogus uniqueness
1578  * failure. In that case we wait for the inserting
1579  * transaction to finish and check again.
1580  */
1581  if (checking_uniqueness)
1582  {
1583  /*
1584  * Must drop the lock on the buffer before we wait
1585  */
1587  XactLockTableWait(xwait, heapRelation,
1588  &heapTuple->t_self,
1591  goto recheck;
1592  }
1593  }
1594  else
1595  {
1596  /*
1597  * For consistency with
1598  * heapam_scan_analyze_next_tuple(), count
1599  * HEAPTUPLE_INSERT_IN_PROGRESS tuples as live only
1600  * when inserted by our own transaction.
1601  */
1602  reltuples += 1;
1603  }
1604 
1605  /*
1606  * We must index such tuples, since if the index build
1607  * commits then they're good.
1608  */
1609  indexIt = true;
1610  tupleIsAlive = true;
1611  break;
1613 
1614  /*
1615  * As with INSERT_IN_PROGRESS case, this is unexpected
1616  * unless it's our own deletion or a system catalog; but
1617  * in anyvisible mode, this tuple is visible.
1618  */
1619  if (anyvisible)
1620  {
1621  indexIt = true;
1622  tupleIsAlive = false;
1623  reltuples += 1;
1624  break;
1625  }
1626 
1627  xwait = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
1629  {
1630  if (!is_system_catalog)
1631  elog(WARNING, "concurrent delete in progress within table \"%s\"",
1632  RelationGetRelationName(heapRelation));
1633 
1634  /*
1635  * If we are performing uniqueness checks, assuming
1636  * the tuple is dead could lead to missing a
1637  * uniqueness violation. In that case we wait for the
1638  * deleting transaction to finish and check again.
1639  *
1640  * Also, if it's a HOT-updated tuple, we should not
1641  * index it but rather the live tuple at the end of
1642  * the HOT-chain. However, the deleting transaction
1643  * could abort, possibly leaving this tuple as live
1644  * after all, in which case it has to be indexed. The
1645  * only way to know what to do is to wait for the
1646  * deleting transaction to finish and check again.
1647  */
1648  if (checking_uniqueness ||
1649  HeapTupleIsHotUpdated(heapTuple))
1650  {
1651  /*
1652  * Must drop the lock on the buffer before we wait
1653  */
1655  XactLockTableWait(xwait, heapRelation,
1656  &heapTuple->t_self,
1659  goto recheck;
1660  }
1661 
1662  /*
1663  * Otherwise index it but don't check for uniqueness,
1664  * the same as a RECENTLY_DEAD tuple.
1665  */
1666  indexIt = true;
1667 
1668  /*
1669  * Count HEAPTUPLE_DELETE_IN_PROGRESS tuples as live,
1670  * if they were not deleted by the current
1671  * transaction. That's what
1672  * heapam_scan_analyze_next_tuple() does, and we want
1673  * the behavior to be consistent.
1674  */
1675  reltuples += 1;
1676  }
1677  else if (HeapTupleIsHotUpdated(heapTuple))
1678  {
1679  /*
1680  * It's a HOT-updated tuple deleted by our own xact.
1681  * We can assume the deletion will commit (else the
1682  * index contents don't matter), so treat the same as
1683  * RECENTLY_DEAD HOT-updated tuples.
1684  */
1685  indexIt = false;
1686  /* mark the index as unsafe for old snapshots */
1687  indexInfo->ii_BrokenHotChain = true;
1688  }
1689  else
1690  {
1691  /*
1692  * It's a regular tuple deleted by our own xact. Index
1693  * it, but don't check for uniqueness nor count in
1694  * reltuples, the same as a RECENTLY_DEAD tuple.
1695  */
1696  indexIt = true;
1697  }
1698  /* In any case, exclude the tuple from unique-checking */
1699  tupleIsAlive = false;
1700  break;
1701  default:
1702  elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1703  indexIt = tupleIsAlive = false; /* keep compiler quiet */
1704  break;
1705  }
1706 
1708 
1709  if (!indexIt)
1710  continue;
1711  }
1712  else
1713  {
1714  /* heap_getnext did the time qual check */
1715  tupleIsAlive = true;
1716  reltuples += 1;
1717  }
1718 
1720 
1721  /* Set up for predicate or expression evaluation */
1722  ExecStoreBufferHeapTuple(heapTuple, slot, hscan->rs_cbuf);
1723 
1724  /*
1725  * In a partial index, discard tuples that don't satisfy the
1726  * predicate.
1727  */
1728  if (predicate != NULL)
1729  {
1730  if (!ExecQual(predicate, econtext))
1731  continue;
1732  }
1733 
1734  /*
1735  * For the current heap tuple, extract all the attributes we use in
1736  * this index, and note which are null. This also performs evaluation
1737  * of any expressions needed.
1738  */
1739  FormIndexDatum(indexInfo,
1740  slot,
1741  estate,
1742  values,
1743  isnull);
1744 
1745  /*
1746  * You'd think we should go ahead and build the index tuple here, but
1747  * some index AMs want to do further processing on the data first. So
1748  * pass the values[] and isnull[] arrays, instead.
1749  */
1750 
1751  if (HeapTupleIsHeapOnly(heapTuple))
1752  {
1753  /*
1754  * For a heap-only tuple, pretend its TID is that of the root. See
1755  * src/backend/access/heap/README.HOT for discussion.
1756  */
1757  ItemPointerData tid;
1758  OffsetNumber offnum;
1759 
1760  offnum = ItemPointerGetOffsetNumber(&heapTuple->t_self);
1761 
1762  /*
1763  * If a HOT tuple points to a root that we don't know about,
1764  * obtain root items afresh. If that still fails, report it as
1765  * corruption.
1766  */
1767  if (root_offsets[offnum - 1] == InvalidOffsetNumber)
1768  {
1769  Page page = BufferGetPage(hscan->rs_cbuf);
1770 
1772  heap_get_root_tuples(page, root_offsets);
1774  }
1775 
1776  if (!OffsetNumberIsValid(root_offsets[offnum - 1]))
1777  ereport(ERROR,
1779  errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1780  ItemPointerGetBlockNumber(&heapTuple->t_self),
1781  offnum,
1782  RelationGetRelationName(heapRelation))));
1783 
1784  ItemPointerSet(&tid, ItemPointerGetBlockNumber(&heapTuple->t_self),
1785  root_offsets[offnum - 1]);
1786 
1787  /* Call the AM's callback routine to process the tuple */
1788  callback(indexRelation, &tid, values, isnull, tupleIsAlive,
1789  callback_state);
1790  }
1791  else
1792  {
1793  /* Call the AM's callback routine to process the tuple */
1794  callback(indexRelation, &heapTuple->t_self, values, isnull,
1795  tupleIsAlive, callback_state);
1796  }
1797  }
1798 
1799  /* Report scan progress one last time. */
1800  if (progress)
1801  {
1802  BlockNumber blks_done;
1803 
1804  if (hscan->rs_base.rs_parallel != NULL)
1805  {
1807 
1809  blks_done = pbscan->phs_nblocks;
1810  }
1811  else
1812  blks_done = hscan->rs_nblocks;
1813 
1815  blks_done);
1816  }
1817 
1818  table_endscan(scan);
1819 
1820  /* we can now forget our snapshot, if set and registered by us */
1821  if (need_unregister_snapshot)
1822  UnregisterSnapshot(snapshot);
1823 
1825 
1826  FreeExecutorState(estate);
1827 
1828  /* These may have been pointing to the now-gone estate */
1829  indexInfo->ii_ExpressionsState = NIL;
1830  indexInfo->ii_PredicateState = NULL;
1831 
1832  return reltuples;
1833 }
void pgstat_progress_update_param(int index, int64 val)
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
static Datum values[MAXATTR]
Definition: bootstrap.c:156
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4226
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:110
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:111
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:284
Pointer Page
Definition: bufpage.h:78
uint32 TransactionId
Definition: c.h:636
#define OidIsValid(objectId)
Definition: c.h:759
bool IsSystemRelation(Relation relation)
Definition: catalog.c:75
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1156
int errcode(int sqlerrcode)
Definition: elog.c:858
#define WARNING
Definition: elog.h:36
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
ExprState * ExecPrepareQual(List *qual, EState *estate)
Definition: execExpr.c:763
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1255
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
Definition: execTuples.c:1393
EState * CreateExecutorState(void)
Definition: execUtils.c:93
void FreeExecutorState(EState *estate)
Definition: execUtils.c:194
#define GetPerTupleExprContext(estate)
Definition: executor.h:548
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:411
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1093
void heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
Definition: heapam.c:354
struct HeapScanDescData * HeapScanDesc
Definition: heapam.h:80
@ HEAPTUPLE_RECENTLY_DEAD
Definition: heapam.h:98
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition: heapam.h:99
@ HEAPTUPLE_LIVE
Definition: heapam.h:97
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition: heapam.h:100
@ HEAPTUPLE_DEAD
Definition: heapam.h:96
static BlockNumber heapam_scan_get_blocks_done(HeapScanDesc hscan)
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:309
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:683
#define HeapTupleIsHotUpdated(tuple)
Definition: htup_details.h:674
#define MaxHeapTuplesPerPage
Definition: htup_details.h:572
#define HeapTupleHeaderGetUpdateXid(tup)
Definition: htup_details.h:361
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition: index.c:2721
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition: itemptr.h:135
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper)
Definition: lmgr.c:668
@ XLTW_InsertIndexUnique
Definition: lmgr.h:32
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:314
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:405
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
uint16 OffsetNumber
Definition: off.h:24
#define ERRCODE_DATA_CORRUPTED
Definition: pg_basebackup.c:41
#define INDEX_MAX_KEYS
#define NIL
Definition: pg_list.h:68
int progress
Definition: pgbench.c:271
uintptr_t Datum
Definition: postgres.h:64
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition: procarray.c:2034
#define PROGRESS_SCAN_BLOCKS_DONE
Definition: progress.h:120
#define PROGRESS_SCAN_BLOCKS_TOTAL
Definition: progress.h:119
void heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
Definition: pruneheap.c:1111
#define RelationGetRelationName(relation)
Definition: rel.h:537
struct ParallelBlockTableScanDescData * ParallelBlockTableScanDesc
Definition: relscan.h:85
@ ForwardScanDirection
Definition: sdir.h:28
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:251
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:871
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:829
#define SnapshotAny
Definition: snapmgr.h:67
#define IsMVCCSnapshot(snapshot)
Definition: snapmgr.h:96
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:257
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:249
Buffer rs_cbuf
Definition: heapam.h:62
BlockNumber rs_nblocks
Definition: heapam.h:53
BlockNumber rs_cblock
Definition: heapam.h:61
TableScanDescData rs_base
Definition: heapam.h:50
ItemPointerData t_self
Definition: htup.h:65
HeapTupleHeader t_data
Definition: htup.h:68
bool ii_Unique
Definition: execnodes.h:191
bool ii_BrokenHotChain
Definition: execnodes.h:197
ExprState * ii_PredicateState
Definition: execnodes.h:183
Oid * ii_ExclusionOps
Definition: execnodes.h:184
bool ii_Concurrent
Definition: execnodes.h:196
List * ii_ExpressionsState
Definition: execnodes.h:181
List * ii_Predicate
Definition: execnodes.h:182
Form_pg_class rd_rel
Definition: rel.h:110
struct SnapshotData * rs_snapshot
Definition: relscan.h:35
struct ParallelTableScanDescData * rs_parallel
Definition: relscan.h:49
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1011
static TableScanDesc table_beginscan_strat(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key, bool allow_strat, bool allow_sync)
Definition: tableam.h:927
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:46
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdIsValid(xid)
Definition: transam.h:41
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:926

References Assert(), BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), callback(), CHECK_FOR_INTERRUPTS, CreateExecutorState(), ExprContext::ecxt_per_tuple_memory, ExprContext::ecxt_scantuple, elog(), ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), ERROR, ExecDropSingleTupleTableSlot(), ExecPrepareQual(), ExecQual(), ExecStoreBufferHeapTuple(), FormIndexDatum(), ForwardScanDirection, FreeExecutorState(), GetOldestNonRemovableTransactionId(), GetPerTupleExprContext, GetTransactionSnapshot(), heap_get_root_tuples(), heap_getnext(), heap_setscanlimits(), heapam_scan_get_blocks_done(), HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleIsHeapOnly, HeapTupleIsHotUpdated, HeapTupleSatisfiesVacuum(), IndexInfo::ii_BrokenHotChain, IndexInfo::ii_Concurrent, IndexInfo::ii_ExclusionOps, IndexInfo::ii_ExpressionsState, IndexInfo::ii_Predicate, IndexInfo::ii_PredicateState, IndexInfo::ii_Unique, INDEX_MAX_KEYS, InvalidBlockNumber, InvalidOffsetNumber, InvalidTransactionId, IsBootstrapProcessingMode, IsMVCCSnapshot, IsSystemRelation(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerSet(), LockBuffer(), MaxHeapTuplesPerPage, MemoryContextReset(), NIL, OffsetNumberIsValid, OidIsValid, pgstat_progress_update_param(), ParallelBlockTableScanDescData::phs_nblocks, progress, PROGRESS_SCAN_BLOCKS_DONE, PROGRESS_SCAN_BLOCKS_TOTAL, RelationData::rd_rel, RegisterSnapshot(), RelationGetRelationName, HeapScanDescData::rs_base, HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_nblocks, TableScanDescData::rs_parallel, TableScanDescData::rs_snapshot, SnapshotAny, HeapTupleData::t_data, HeapTupleData::t_self, table_beginscan_strat(), table_endscan(), table_slot_create(), TransactionIdIsCurrentTransactionId(), TransactionIdIsValid, UnregisterSnapshot(), values, WARNING, XactLockTableWait(), and XLTW_InsertIndexUnique.

◆ heapam_index_fetch_begin()

static IndexFetchTableData* heapam_index_fetch_begin ( Relation  rel)
static

Definition at line 85 of file heapam_handler.c.

86 {
88 
89  hscan->xs_base.rel = rel;
90  hscan->xs_cbuf = InvalidBuffer;
91 
92  return &hscan->xs_base;
93 }
#define InvalidBuffer
Definition: buf.h:25
void * palloc0(Size size)
Definition: mcxt.c:1241
Buffer xs_cbuf
Definition: heapam.h:89
IndexFetchTableData xs_base
Definition: heapam.h:87

References InvalidBuffer, palloc0(), IndexFetchTableData::rel, IndexFetchHeapData::xs_base, and IndexFetchHeapData::xs_cbuf.

◆ heapam_index_fetch_end()

static void heapam_index_fetch_end ( IndexFetchTableData scan)
static

Definition at line 108 of file heapam_handler.c.

109 {
110  IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
111 
113 
114  pfree(hscan);
115 }
static void heapam_index_fetch_reset(IndexFetchTableData *scan)
void pfree(void *pointer)
Definition: mcxt.c:1436

References heapam_index_fetch_reset(), and pfree().

◆ heapam_index_fetch_reset()

static void heapam_index_fetch_reset ( IndexFetchTableData scan)
static

Definition at line 96 of file heapam_handler.c.

97 {
98  IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
99 
100  if (BufferIsValid(hscan->xs_cbuf))
101  {
102  ReleaseBuffer(hscan->xs_cbuf);
103  hscan->xs_cbuf = InvalidBuffer;
104  }
105 }
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3985
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:232

References BufferIsValid(), InvalidBuffer, ReleaseBuffer(), and IndexFetchHeapData::xs_cbuf.

Referenced by heapam_index_fetch_end().

◆ heapam_index_fetch_tuple()

static bool heapam_index_fetch_tuple ( struct IndexFetchTableData scan,
ItemPointer  tid,
Snapshot  snapshot,
TupleTableSlot slot,
bool call_again,
bool all_dead 
)
static

Definition at line 118 of file heapam_handler.c.

123 {
124  IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
126  bool got_heap_tuple;
127 
128  Assert(TTS_IS_BUFFERTUPLE(slot));
129 
130  /* We can skip the buffer-switching logic if we're in mid-HOT chain. */
131  if (!*call_again)
132  {
133  /* Switch to correct buffer if we don't have it already */
134  Buffer prev_buf = hscan->xs_cbuf;
135 
136  hscan->xs_cbuf = ReleaseAndReadBuffer(hscan->xs_cbuf,
137  hscan->xs_base.rel,
139 
140  /*
141  * Prune page, but only if we weren't already on this page
142  */
143  if (prev_buf != hscan->xs_cbuf)
144  heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf);
145  }
146 
147  /* Obtain share-lock on the buffer so we can examine visibility */
149  got_heap_tuple = heap_hot_search_buffer(tid,
150  hscan->xs_base.rel,
151  hscan->xs_cbuf,
152  snapshot,
153  &bslot->base.tupdata,
154  all_dead,
155  !*call_again);
156  bslot->base.tupdata.t_self = *tid;
158 
159  if (got_heap_tuple)
160  {
161  /*
162  * Only in a non-MVCC snapshot can more than one member of the HOT
163  * chain be visible.
164  */
165  *call_again = !IsMVCCSnapshot(snapshot);
166 
167  slot->tts_tableOid = RelationGetRelid(scan->rel);
168  ExecStoreBufferHeapTuple(&bslot->base.tupdata, slot, hscan->xs_cbuf);
169  }
170  else
171  {
172  /* We've reached the end of the HOT chain. */
173  *call_again = false;
174  }
175 
176  return got_heap_tuple;
177 }
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
Definition: bufmgr.c:1684
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
Definition: heapam.c:1473
void heap_page_prune_opt(Relation relation, Buffer buffer)
Definition: pruneheap.c:108

References Assert(), BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, ExecStoreBufferHeapTuple(), heap_hot_search_buffer(), heap_page_prune_opt(), IsMVCCSnapshot, ItemPointerGetBlockNumber(), LockBuffer(), IndexFetchTableData::rel, RelationGetRelid, ReleaseAndReadBuffer(), TTS_IS_BUFFERTUPLE, TupleTableSlot::tts_tableOid, IndexFetchHeapData::xs_base, and IndexFetchHeapData::xs_cbuf.

◆ heapam_index_validate_scan()

static void heapam_index_validate_scan ( Relation  heapRelation,
Relation  indexRelation,
IndexInfo indexInfo,
Snapshot  snapshot,
ValidateIndexState state 
)
static

Definition at line 1836 of file heapam_handler.c.

1841 {
1842  TableScanDesc scan;
1843  HeapScanDesc hscan;
1844  HeapTuple heapTuple;
1846  bool isnull[INDEX_MAX_KEYS];
1847  ExprState *predicate;
1848  TupleTableSlot *slot;
1849  EState *estate;
1850  ExprContext *econtext;
1851  BlockNumber root_blkno = InvalidBlockNumber;
1852  OffsetNumber root_offsets[MaxHeapTuplesPerPage];
1853  bool in_index[MaxHeapTuplesPerPage];
1854  BlockNumber previous_blkno = InvalidBlockNumber;
1855 
1856  /* state variables for the merge */
1857  ItemPointer indexcursor = NULL;
1858  ItemPointerData decoded;
1859  bool tuplesort_empty = false;
1860 
1861  /*
1862  * sanity checks
1863  */
1864  Assert(OidIsValid(indexRelation->rd_rel->relam));
1865 
1866  /*
1867  * Need an EState for evaluation of index expressions and partial-index
1868  * predicates. Also a slot to hold the current tuple.
1869  */
1870  estate = CreateExecutorState();
1871  econtext = GetPerTupleExprContext(estate);
1872  slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
1873  &TTSOpsHeapTuple);
1874 
1875  /* Arrange for econtext's scan tuple to be the tuple under test */
1876  econtext->ecxt_scantuple = slot;
1877 
1878  /* Set up execution state for predicate, if any. */
1879  predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1880 
1881  /*
1882  * Prepare for scan of the base relation. We need just those tuples
1883  * satisfying the passed-in reference snapshot. We must disable syncscan
1884  * here, because it's critical that we read from block zero forward to
1885  * match the sorted TIDs.
1886  */
1887  scan = table_beginscan_strat(heapRelation, /* relation */
1888  snapshot, /* snapshot */
1889  0, /* number of keys */
1890  NULL, /* scan key */
1891  true, /* buffer access strategy OK */
1892  false); /* syncscan not OK */
1893  hscan = (HeapScanDesc) scan;
1894 
1896  hscan->rs_nblocks);
1897 
1898  /*
1899  * Scan all tuples matching the snapshot.
1900  */
1901  while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1902  {
1903  ItemPointer heapcursor = &heapTuple->t_self;
1904  ItemPointerData rootTuple;
1905  OffsetNumber root_offnum;
1906 
1908 
1909  state->htups += 1;
1910 
1911  if ((previous_blkno == InvalidBlockNumber) ||
1912  (hscan->rs_cblock != previous_blkno))
1913  {
1915  hscan->rs_cblock);
1916  previous_blkno = hscan->rs_cblock;
1917  }
1918 
1919  /*
1920  * As commented in table_index_build_scan, we should index heap-only
1921  * tuples under the TIDs of their root tuples; so when we advance onto
1922  * a new heap page, build a map of root item offsets on the page.
1923  *
1924  * This complicates merging against the tuplesort output: we will
1925  * visit the live tuples in order by their offsets, but the root
1926  * offsets that we need to compare against the index contents might be
1927  * ordered differently. So we might have to "look back" within the
1928  * tuplesort output, but only within the current page. We handle that
1929  * by keeping a bool array in_index[] showing all the
1930  * already-passed-over tuplesort output TIDs of the current page. We
1931  * clear that array here, when advancing onto a new heap page.
1932  */
1933  if (hscan->rs_cblock != root_blkno)
1934  {
1935  Page page = BufferGetPage(hscan->rs_cbuf);
1936 
1938  heap_get_root_tuples(page, root_offsets);
1940 
1941  memset(in_index, 0, sizeof(in_index));
1942 
1943  root_blkno = hscan->rs_cblock;
1944  }
1945 
1946  /* Convert actual tuple TID to root TID */
1947  rootTuple = *heapcursor;
1948  root_offnum = ItemPointerGetOffsetNumber(heapcursor);
1949 
1950  if (HeapTupleIsHeapOnly(heapTuple))
1951  {
1952  root_offnum = root_offsets[root_offnum - 1];
1953  if (!OffsetNumberIsValid(root_offnum))
1954  ereport(ERROR,
1956  errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1957  ItemPointerGetBlockNumber(heapcursor),
1958  ItemPointerGetOffsetNumber(heapcursor),
1959  RelationGetRelationName(heapRelation))));
1960  ItemPointerSetOffsetNumber(&rootTuple, root_offnum);
1961  }
1962 
1963  /*
1964  * "merge" by skipping through the index tuples until we find or pass
1965  * the current root tuple.
1966  */
1967  while (!tuplesort_empty &&
1968  (!indexcursor ||
1969  ItemPointerCompare(indexcursor, &rootTuple) < 0))
1970  {
1971  Datum ts_val;
1972  bool ts_isnull;
1973 
1974  if (indexcursor)
1975  {
1976  /*
1977  * Remember index items seen earlier on the current heap page
1978  */
1979  if (ItemPointerGetBlockNumber(indexcursor) == root_blkno)
1980  in_index[ItemPointerGetOffsetNumber(indexcursor) - 1] = true;
1981  }
1982 
1983  tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true,
1984  false, &ts_val, &ts_isnull,
1985  NULL);
1986  Assert(tuplesort_empty || !ts_isnull);
1987  if (!tuplesort_empty)
1988  {
1989  itemptr_decode(&decoded, DatumGetInt64(ts_val));
1990  indexcursor = &decoded;
1991  }
1992  else
1993  {
1994  /* Be tidy */
1995  indexcursor = NULL;
1996  }
1997  }
1998 
1999  /*
2000  * If the tuplesort has overshot *and* we didn't see a match earlier,
2001  * then this tuple is missing from the index, so insert it.
2002  */
2003  if ((tuplesort_empty ||
2004  ItemPointerCompare(indexcursor, &rootTuple) > 0) &&
2005  !in_index[root_offnum - 1])
2006  {
2008 
2009  /* Set up for predicate or expression evaluation */
2010  ExecStoreHeapTuple(heapTuple, slot, false);
2011 
2012  /*
2013  * In a partial index, discard tuples that don't satisfy the
2014  * predicate.
2015  */
2016  if (predicate != NULL)
2017  {
2018  if (!ExecQual(predicate, econtext))
2019  continue;
2020  }
2021 
2022  /*
2023  * For the current heap tuple, extract all the attributes we use
2024  * in this index, and note which are null. This also performs
2025  * evaluation of any expressions needed.
2026  */
2027  FormIndexDatum(indexInfo,
2028  slot,
2029  estate,
2030  values,
2031  isnull);
2032 
2033  /*
2034  * You'd think we should go ahead and build the index tuple here,
2035  * but some index AMs want to do further processing on the data
2036  * first. So pass the values[] and isnull[] arrays, instead.
2037  */
2038 
2039  /*
2040  * If the tuple is already committed dead, you might think we
2041  * could suppress uniqueness checking, but this is no longer true
2042  * in the presence of HOT, because the insert is actually a proxy
2043  * for a uniqueness check on the whole HOT-chain. That is, the
2044  * tuple we have here could be dead because it was already
2045  * HOT-updated, and if so the updating transaction will not have
2046  * thought it should insert index entries. The index AM will
2047  * check the whole HOT-chain and correctly detect a conflict if
2048  * there is one.
2049  */
2050 
2051  index_insert(indexRelation,
2052  values,
2053  isnull,
2054  &rootTuple,
2055  heapRelation,
2056  indexInfo->ii_Unique ?
2058  false,
2059  indexInfo);
2060 
2061  state->tups_inserted += 1;
2062  }
2063  }
2064 
2065  table_endscan(scan);
2066 
2068 
2069  FreeExecutorState(estate);
2070 
2071  /* These may have been pointing to the now-gone estate */
2072  indexInfo->ii_ExpressionsState = NIL;
2073  indexInfo->ii_PredicateState = NULL;
2074 }
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1353
const TupleTableSlotOps TTSOpsHeapTuple
Definition: execTuples.c:84
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1239
@ UNIQUE_CHECK_NO
Definition: genam.h:116
@ UNIQUE_CHECK_YES
Definition: genam.h:117
static void itemptr_decode(ItemPointer itemptr, int64 encoded)
Definition: index.h:206
bool index_insert(Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_t_ctid, Relation heapRelation, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition: indexam.c:176
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:51
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition: itemptr.h:158
static int64 DatumGetInt64(Datum X)
Definition: postgres.h:385
#define RelationGetDescr(relation)
Definition: rel.h:529
Definition: regguts.h:318
bool tuplesort_getdatum(Tuplesortstate *state, bool forward, bool copy, Datum *val, bool *isNull, Datum *abbrev)

References Assert(), BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), CHECK_FOR_INTERRUPTS, CreateExecutorState(), DatumGetInt64(), ExprContext::ecxt_per_tuple_memory, ExprContext::ecxt_scantuple, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg_internal(), ERROR, ExecDropSingleTupleTableSlot(), ExecPrepareQual(), ExecQual(), ExecStoreHeapTuple(), FormIndexDatum(), ForwardScanDirection, FreeExecutorState(), GetPerTupleExprContext, heap_get_root_tuples(), heap_getnext(), HeapTupleIsHeapOnly, IndexInfo::ii_ExpressionsState, IndexInfo::ii_Predicate, IndexInfo::ii_PredicateState, IndexInfo::ii_Unique, index_insert(), INDEX_MAX_KEYS, InvalidBlockNumber, ItemPointerCompare(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerSetOffsetNumber(), itemptr_decode(), LockBuffer(), MakeSingleTupleTableSlot(), MaxHeapTuplesPerPage, MemoryContextReset(), NIL, OffsetNumberIsValid, OidIsValid, pgstat_progress_update_param(), PROGRESS_SCAN_BLOCKS_DONE, PROGRESS_SCAN_BLOCKS_TOTAL, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_nblocks, HeapTupleData::t_self, table_beginscan_strat(), table_endscan(), TTSOpsHeapTuple, tuplesort_getdatum(), UNIQUE_CHECK_NO, UNIQUE_CHECK_YES, and values.

◆ heapam_relation_copy_data()

static void heapam_relation_copy_data ( Relation  rel,
const RelFileLocator newrlocator 
)
static

Definition at line 727 of file heapam_handler.c.

728 {
729  SMgrRelation dstrel;
730 
731  dstrel = smgropen(*newrlocator, rel->rd_backend);
732 
733  /*
734  * Since we copy the file directly without looking at the shared buffers,
735  * we'd better first flush out any pages of the source relation that are
736  * in shared buffers. We assume no new changes will be made while we are
737  * holding exclusive lock on the rel.
738  */
740 
741  /*
742  * Create and copy all forks of the relation, and schedule unlinking of
743  * old physical files.
744  *
745  * NOTE: any conflict in relfilenumber value will be caught in
746  * RelationCreateStorage().
747  */
748  RelationCreateStorage(*newrlocator, rel->rd_rel->relpersistence, true);
749 
750  /* copy main fork */
752  rel->rd_rel->relpersistence);
753 
754  /* copy those extra forks that exist */
755  for (ForkNumber forkNum = MAIN_FORKNUM + 1;
756  forkNum <= MAX_FORKNUM; forkNum++)
757  {
758  if (smgrexists(RelationGetSmgr(rel), forkNum))
759  {
760  smgrcreate(dstrel, forkNum, false);
761 
762  /*
763  * WAL log creation if the relation is persistent, or this is the
764  * init fork of an unlogged relation.
765  */
766  if (RelationIsPermanent(rel) ||
767  (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
768  forkNum == INIT_FORKNUM))
769  log_smgrcreate(newrlocator, forkNum);
770  RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
771  rel->rd_rel->relpersistence);
772  }
773  }
774 
775 
776  /* drop old relation, and close new one */
777  RelationDropStorage(rel);
778  smgrclose(dstrel);
779 }
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:3570
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:571
#define RelationIsPermanent(relation)
Definition: rel.h:617
ForkNumber
Definition: relpath.h:48
@ MAIN_FORKNUM
Definition: relpath.h:50
@ INIT_FORKNUM
Definition: relpath.h:53
#define MAX_FORKNUM
Definition: relpath.h:62
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:369
void smgrclose(SMgrRelation reln)
Definition: smgr.c:256
SMgrRelation smgropen(RelFileLocator rlocator, BackendId backend)
Definition: smgr.c:146
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:247
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:451
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:120
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:185
void RelationDropStorage(Relation rel)
Definition: storage.c:205
BackendId rd_backend
Definition: rel.h:59

References FlushRelationBuffers(), INIT_FORKNUM, log_smgrcreate(), MAIN_FORKNUM, MAX_FORKNUM, RelationData::rd_backend, RelationData::rd_rel, RelationCopyStorage(), RelationCreateStorage(), RelationDropStorage(), RelationGetSmgr(), RelationIsPermanent, smgrclose(), smgrcreate(), smgrexists(), and smgropen().

◆ heapam_relation_copy_for_cluster()

static void heapam_relation_copy_for_cluster ( Relation  OldHeap,
Relation  NewHeap,
Relation  OldIndex,
bool  use_sort,
TransactionId  OldestXmin,
TransactionId xid_cutoff,
MultiXactId multi_cutoff,
double *  num_tuples,
double *  tups_vacuumed,
double *  tups_recently_dead 
)
static

Definition at line 782 of file heapam_handler.c.

790 {
791  RewriteState rwstate;
792  IndexScanDesc indexScan;
793  TableScanDesc tableScan;
794  HeapScanDesc heapScan;
795  bool is_system_catalog;
796  Tuplesortstate *tuplesort;
797  TupleDesc oldTupDesc = RelationGetDescr(OldHeap);
798  TupleDesc newTupDesc = RelationGetDescr(NewHeap);
799  TupleTableSlot *slot;
800  int natts;
801  Datum *values;
802  bool *isnull;
804  BlockNumber prev_cblock = InvalidBlockNumber;
805 
806  /* Remember if it's a system catalog */
807  is_system_catalog = IsSystemRelation(OldHeap);
808 
809  /*
810  * Valid smgr_targblock implies something already wrote to the relation.
811  * This may be harmless, but this function hasn't planned for it.
812  */
814 
815  /* Preallocate values/isnull arrays */
816  natts = newTupDesc->natts;
817  values = (Datum *) palloc(natts * sizeof(Datum));
818  isnull = (bool *) palloc(natts * sizeof(bool));
819 
820  /* Initialize the rewrite operation */
821  rwstate = begin_heap_rewrite(OldHeap, NewHeap, OldestXmin, *xid_cutoff,
822  *multi_cutoff);
823 
824 
825  /* Set up sorting if wanted */
826  if (use_sort)
827  tuplesort = tuplesort_begin_cluster(oldTupDesc, OldIndex,
829  NULL, TUPLESORT_NONE);
830  else
831  tuplesort = NULL;
832 
833  /*
834  * Prepare to scan the OldHeap. To ensure we see recently-dead tuples
835  * that still need to be copied, we scan with SnapshotAny and use
836  * HeapTupleSatisfiesVacuum for the visibility test.
837  */
838  if (OldIndex != NULL && !use_sort)
839  {
840  const int ci_index[] = {
843  };
844  int64 ci_val[2];
845 
846  /* Set phase and OIDOldIndex to columns */
848  ci_val[1] = RelationGetRelid(OldIndex);
849  pgstat_progress_update_multi_param(2, ci_index, ci_val);
850 
851  tableScan = NULL;
852  heapScan = NULL;
853  indexScan = index_beginscan(OldHeap, OldIndex, SnapshotAny, 0, 0);
854  index_rescan(indexScan, NULL, 0, NULL, 0);
855  }
856  else
857  {
858  /* In scan-and-sort mode and also VACUUM FULL, set phase */
861 
862  tableScan = table_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
863  heapScan = (HeapScanDesc) tableScan;
864  indexScan = NULL;
865 
866  /* Set total heap blocks */
868  heapScan->rs_nblocks);
869  }
870 
871  slot = table_slot_create(OldHeap, NULL);
872  hslot = (BufferHeapTupleTableSlot *) slot;
873 
874  /*
875  * Scan through the OldHeap, either in OldIndex order or sequentially;
876  * copy each tuple into the NewHeap, or transiently to the tuplesort
877  * module. Note that we don't bother sorting dead tuples (they won't get
878  * to the new table anyway).
879  */
880  for (;;)
881  {
882  HeapTuple tuple;
883  Buffer buf;
884  bool isdead;
885 
887 
888  if (indexScan != NULL)
889  {
890  if (!index_getnext_slot(indexScan, ForwardScanDirection, slot))
891  break;
892 
893  /* Since we used no scan keys, should never need to recheck */
894  if (indexScan->xs_recheck)
895  elog(ERROR, "CLUSTER does not support lossy index conditions");
896  }
897  else
898  {
899  if (!table_scan_getnextslot(tableScan, ForwardScanDirection, slot))
900  {
901  /*
902  * If the last pages of the scan were empty, we would go to
903  * the next phase while heap_blks_scanned != heap_blks_total.
904  * Instead, to ensure that heap_blks_scanned is equivalent to
905  * total_heap_blks after the table scan phase, this parameter
906  * is manually updated to the correct value when the table
907  * scan finishes.
908  */
910  heapScan->rs_nblocks);
911  break;
912  }
913 
914  /*
915  * In scan-and-sort mode and also VACUUM FULL, set heap blocks
916  * scanned
917  *
918  * Note that heapScan may start at an offset and wrap around, i.e.
919  * rs_startblock may be >0, and rs_cblock may end with a number
920  * below rs_startblock. To prevent showing this wraparound to the
921  * user, we offset rs_cblock by rs_startblock (modulo rs_nblocks).
922  */
923  if (prev_cblock != heapScan->rs_cblock)
924  {
926  (heapScan->rs_cblock +
927  heapScan->rs_nblocks -
928  heapScan->rs_startblock
929  ) % heapScan->rs_nblocks + 1);
930  prev_cblock = heapScan->rs_cblock;
931  }
932  }
933 
934  tuple = ExecFetchSlotHeapTuple(slot, false, NULL);
935  buf = hslot->buffer;
936 
938 
939  switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
940  {
941  case HEAPTUPLE_DEAD:
942  /* Definitely dead */
943  isdead = true;
944  break;
946  *tups_recently_dead += 1;
947  /* fall through */
948  case HEAPTUPLE_LIVE:
949  /* Live or recently dead, must copy it */
950  isdead = false;
951  break;
953 
954  /*
955  * Since we hold exclusive lock on the relation, normally the
956  * only way to see this is if it was inserted earlier in our
957  * own transaction. However, it can happen in system
958  * catalogs, since we tend to release write lock before commit
959  * there. Give a warning if neither case applies; but in any
960  * case we had better copy it.
961  */
962  if (!is_system_catalog &&
964  elog(WARNING, "concurrent insert in progress within table \"%s\"",
965  RelationGetRelationName(OldHeap));
966  /* treat as live */
967  isdead = false;
968  break;
970 
971  /*
972  * Similar situation to INSERT_IN_PROGRESS case.
973  */
974  if (!is_system_catalog &&
976  elog(WARNING, "concurrent delete in progress within table \"%s\"",
977  RelationGetRelationName(OldHeap));
978  /* treat as recently dead */
979  *tups_recently_dead += 1;
980  isdead = false;
981  break;
982  default:
983  elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
984  isdead = false; /* keep compiler quiet */
985  break;
986  }
987 
989 
990  if (isdead)
991  {
992  *tups_vacuumed += 1;
993  /* heap rewrite module still needs to see it... */
994  if (rewrite_heap_dead_tuple(rwstate, tuple))
995  {
996  /* A previous recently-dead tuple is now known dead */
997  *tups_vacuumed += 1;
998  *tups_recently_dead -= 1;
999  }
1000  continue;
1001  }
1002 
1003  *num_tuples += 1;
1004  if (tuplesort != NULL)
1005  {
1006  tuplesort_putheaptuple(tuplesort, tuple);
1007 
1008  /*
1009  * In scan-and-sort mode, report increase in number of tuples
1010  * scanned
1011  */
1013  *num_tuples);
1014  }
1015  else
1016  {
1017  const int ct_index[] = {
1020  };
1021  int64 ct_val[2];
1022 
1023  reform_and_rewrite_tuple(tuple, OldHeap, NewHeap,
1024  values, isnull, rwstate);
1025 
1026  /*
1027  * In indexscan mode and also VACUUM FULL, report increase in
1028  * number of tuples scanned and written
1029  */
1030  ct_val[0] = *num_tuples;
1031  ct_val[1] = *num_tuples;
1032  pgstat_progress_update_multi_param(2, ct_index, ct_val);
1033  }
1034  }
1035 
1036  if (indexScan != NULL)
1037  index_endscan(indexScan);
1038  if (tableScan != NULL)
1039  table_endscan(tableScan);
1040  if (slot)
1042 
1043  /*
1044  * In scan-and-sort mode, complete the sort, then read out all live tuples
1045  * from the tuplestore and write them to the new relation.
1046  */
1047  if (tuplesort != NULL)
1048  {
1049  double n_tuples = 0;
1050 
1051  /* Report that we are now sorting tuples */
1054 
1055  tuplesort_performsort(tuplesort);
1056 
1057  /* Report that we are now writing new heap */
1060 
1061  for (;;)
1062  {
1063  HeapTuple tuple;
1064 
1066 
1067  tuple = tuplesort_getheaptuple(tuplesort, true);
1068  if (tuple == NULL)
1069  break;
1070 
1071  n_tuples += 1;
1073  OldHeap, NewHeap,
1074  values, isnull,
1075  rwstate);
1076  /* Report n_tuples */
1078  n_tuples);
1079  }
1080 
1081  tuplesort_end(tuplesort);
1082  }
1083 
1084  /* Write out any remaining tuples, and fsync if needed */
1085  end_heap_rewrite(rwstate);
1086 
1087  /* Clean up */
1088  pfree(values);
1089  pfree(isnull);
1090 }
void pgstat_progress_update_multi_param(int nparam, const int *index, const int64 *val)
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1645
int maintenance_work_mem
Definition: globals.c:127
static void reform_and_rewrite_tuple(HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull, RewriteState rwstate)
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
Definition: indexam.c:624
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, int norderbys)
Definition: indexam.c:205
void index_endscan(IndexScanDesc scan)
Definition: indexam.c:327
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition: indexam.c:301
void * palloc(Size size)
Definition: mcxt.c:1210
static char * buf
Definition: pg_test_fsync.c:67
#define PROGRESS_CLUSTER_INDEX_RELID
Definition: progress.h:57
#define PROGRESS_CLUSTER_HEAP_BLKS_SCANNED
Definition: progress.h:61
#define PROGRESS_CLUSTER_PHASE_SORT_TUPLES
Definition: progress.h:67
#define PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP
Definition: progress.h:65
#define PROGRESS_CLUSTER_PHASE
Definition: progress.h:56
#define PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED
Definition: progress.h:58
#define PROGRESS_CLUSTER_TOTAL_HEAP_BLKS
Definition: progress.h:60
#define PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN
Definition: progress.h:59
#define PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP
Definition: progress.h:66
#define PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP
Definition: progress.h:68
#define RelationGetTargetBlock(relation)
Definition: rel.h:601
void end_heap_rewrite(RewriteState state)
Definition: rewriteheap.c:299
bool rewrite_heap_dead_tuple(RewriteState state, HeapTuple old_tuple)
Definition: rewriteheap.c:562
RewriteState begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xmin, TransactionId freeze_xid, MultiXactId cutoff_multi)
Definition: rewriteheap.c:236
BlockNumber rs_startblock
Definition: heapam.h:54
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:903
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1052
void tuplesort_performsort(Tuplesortstate *state)
Definition: tuplesort.c:1385
void tuplesort_end(Tuplesortstate *state)
Definition: tuplesort.c:972
#define TUPLESORT_NONE
Definition: tuplesort.h:92
HeapTuple tuplesort_getheaptuple(Tuplesortstate *state, bool forward)
Tuplesortstate * tuplesort_begin_cluster(TupleDesc tupDesc, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)
void tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)

References Assert(), begin_heap_rewrite(), buf, BufferHeapTupleTableSlot::buffer, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, CHECK_FOR_INTERRUPTS, elog(), end_heap_rewrite(), ERROR, ExecDropSingleTupleTableSlot(), ExecFetchSlotHeapTuple(), ForwardScanDirection, HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleSatisfiesVacuum(), index_beginscan(), index_endscan(), index_getnext_slot(), index_rescan(), InvalidBlockNumber, IsSystemRelation(), LockBuffer(), maintenance_work_mem, TupleDescData::natts, palloc(), pfree(), pgstat_progress_update_multi_param(), pgstat_progress_update_param(), PROGRESS_CLUSTER_HEAP_BLKS_SCANNED, PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED, PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN, PROGRESS_CLUSTER_INDEX_RELID, PROGRESS_CLUSTER_PHASE, PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP, PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP, PROGRESS_CLUSTER_PHASE_SORT_TUPLES, PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP, PROGRESS_CLUSTER_TOTAL_HEAP_BLKS, reform_and_rewrite_tuple(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, RelationGetTargetBlock, rewrite_heap_dead_tuple(), HeapScanDescData::rs_cblock, HeapScanDescData::rs_nblocks, HeapScanDescData::rs_startblock, SnapshotAny, HeapTupleData::t_data, table_beginscan(), table_endscan(), table_scan_getnextslot(), table_slot_create(), TransactionIdIsCurrentTransactionId(), tuplesort_begin_cluster(), tuplesort_end(), tuplesort_getheaptuple(), TUPLESORT_NONE, tuplesort_performsort(), tuplesort_putheaptuple(), values, WARNING, and IndexScanDescData::xs_recheck.

◆ heapam_relation_needs_toast_table()

static bool heapam_relation_needs_toast_table ( Relation  rel)
static

Definition at line 2128 of file heapam_handler.c.

2129 {
2130  int32 data_length = 0;
2131  bool maxlength_unknown = false;
2132  bool has_toastable_attrs = false;
2133  TupleDesc tupdesc = rel->rd_att;
2134  int32 tuple_length;
2135  int i;
2136 
2137  for (i = 0; i < tupdesc->natts; i++)
2138  {
2139  Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2140 
2141  if (att->attisdropped)
2142  continue;
2143  data_length = att_align_nominal(data_length, att->attalign);
2144  if (att->attlen > 0)
2145  {
2146  /* Fixed-length types are never toastable */
2147  data_length += att->attlen;
2148  }
2149  else
2150  {
2151  int32 maxlen = type_maximum_size(att->atttypid,
2152  att->atttypmod);
2153 
2154  if (maxlen < 0)
2155  maxlength_unknown = true;
2156  else
2157  data_length += maxlen;
2158  if (att->attstorage != TYPSTORAGE_PLAIN)
2159  has_toastable_attrs = true;
2160  }
2161  }
2162  if (!has_toastable_attrs)
2163  return false; /* nothing to toast? */
2164  if (maxlength_unknown)
2165  return true; /* any unlimited-length attrs? */
2166  tuple_length = MAXALIGN(SizeofHeapTupleHeader +
2167  BITMAPLEN(tupdesc->natts)) +
2168  MAXALIGN(data_length);
2169  return (tuple_length > TOAST_TUPLE_THRESHOLD);
2170 }
#define MAXALIGN(LEN)
Definition: c.h:795
signed int int32
Definition: c.h:478
int32 type_maximum_size(Oid type_oid, int32 typemod)
Definition: format_type.c:408
#define TOAST_TUPLE_THRESHOLD
Definition: heaptoast.h:48
#define SizeofHeapTupleHeader
Definition: htup_details.h:185
#define BITMAPLEN(NATTS)
Definition: htup_details.h:545
int i
Definition: isn.c:73
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
TupleDesc rd_att
Definition: rel.h:111
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define att_align_nominal(cur_offset, attalign)
Definition: tupmacs.h:129

References att_align_nominal, BITMAPLEN, i, MAXALIGN, TupleDescData::natts, RelationData::rd_att, SizeofHeapTupleHeader, TOAST_TUPLE_THRESHOLD, TupleDescAttr, and type_maximum_size().

◆ heapam_relation_nontransactional_truncate()

static void heapam_relation_nontransactional_truncate ( Relation  rel)
static

Definition at line 721 of file heapam_handler.c.

722 {
723  RelationTruncate(rel, 0);
724 }
void RelationTruncate(Relation rel, BlockNumber nblocks)
Definition: storage.c:287

References RelationTruncate().

◆ heapam_relation_set_new_filelocator()

static void heapam_relation_set_new_filelocator ( Relation  rel,
const RelFileLocator newrlocator,
char  persistence,
TransactionId freezeXid,
MultiXactId minmulti 
)
static

Definition at line 671 of file heapam_handler.c.

676 {
677  SMgrRelation srel;
678 
679  /*
680  * Initialize to the minimum XID that could put tuples in the table. We
681  * know that no xacts older than RecentXmin are still running, so that
682  * will do.
683  */
684  *freezeXid = RecentXmin;
685 
686  /*
687  * Similarly, initialize the minimum Multixact to the first value that
688  * could possibly be stored in tuples in the table. Running transactions
689  * could reuse values from their local cache, so we are careful to
690  * consider all currently running multis.
691  *
692  * XXX this could be refined further, but is it worth the hassle?
693  */
694  *minmulti = GetOldestMultiXactId();
695 
696  srel = RelationCreateStorage(*newrlocator, persistence, true);
697 
698  /*
699  * If required, set up an init fork for an unlogged table so that it can
700  * be correctly reinitialized on restart. An immediate sync is required
701  * even if the page has been logged, because the write did not go through
702  * shared_buffers and therefore a concurrent checkpoint may have moved the
703  * redo pointer past our xlog record. Recovery may as well remove it
704  * while replaying, for example, XLOG_DBASE_CREATE* or XLOG_TBLSPC_CREATE
705  * record. Therefore, logging is necessary even if wal_level=minimal.
706  */
707  if (persistence == RELPERSISTENCE_UNLOGGED)
708  {
709  Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
710  rel->rd_rel->relkind == RELKIND_MATVIEW ||
711  rel->rd_rel->relkind == RELKIND_TOASTVALUE);
712  smgrcreate(srel, INIT_FORKNUM, false);
713  log_smgrcreate(newrlocator, INIT_FORKNUM);
715  }
716 
717  smgrclose(srel);
718 }
MultiXactId GetOldestMultiXactId(void)
Definition: multixact.c:2507
void smgrimmedsync(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:691
TransactionId RecentXmin
Definition: snapmgr.c:114

References Assert(), GetOldestMultiXactId(), INIT_FORKNUM, log_smgrcreate(), RelationData::rd_rel, RecentXmin, RelationCreateStorage(), smgrclose(), smgrcreate(), and smgrimmedsync().

◆ heapam_relation_toast_am()

static Oid heapam_relation_toast_am ( Relation  rel)
static

Definition at line 2176 of file heapam_handler.c.

2177 {
2178  return rel->rd_rel->relam;
2179 }

References RelationData::rd_rel.

◆ heapam_scan_analyze_next_block()

static bool heapam_scan_analyze_next_block ( TableScanDesc  scan,
BlockNumber  blockno,
BufferAccessStrategy  bstrategy 
)
static

Definition at line 1093 of file heapam_handler.c.

1095 {
1096  HeapScanDesc hscan = (HeapScanDesc) scan;
1097 
1098  /*
1099  * We must maintain a pin on the target page's buffer to ensure that
1100  * concurrent activity - e.g. HOT pruning - doesn't delete tuples out from
1101  * under us. Hence, pin the page until we are done looking at it. We
1102  * also choose to hold sharelock on the buffer throughout --- we could
1103  * release and re-acquire sharelock for each tuple, but since we aren't
1104  * doing much work per tuple, the extra lock traffic is probably better
1105  * avoided.
1106  */
1107  hscan->rs_cblock = blockno;
1108  hscan->rs_cindex = FirstOffsetNumber;
1109  hscan->rs_cbuf = ReadBufferExtended(scan->rs_rd, MAIN_FORKNUM,
1110  blockno, RBM_NORMAL, bstrategy);
1112 
1113  /* in heap all blocks can contain tuples, so always return true */
1114  return true;
1115 }
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:751
@ RBM_NORMAL
Definition: bufmgr.h:44
#define FirstOffsetNumber
Definition: off.h:27
Relation rs_rd
Definition: relscan.h:34

References BUFFER_LOCK_SHARE, FirstOffsetNumber, LockBuffer(), MAIN_FORKNUM, RBM_NORMAL, ReadBufferExtended(), HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_cindex, and TableScanDescData::rs_rd.

◆ heapam_scan_analyze_next_tuple()

static bool heapam_scan_analyze_next_tuple ( TableScanDesc  scan,
TransactionId  OldestXmin,
double *  liverows,
double *  deadrows,
TupleTableSlot slot 
)
static

Definition at line 1118 of file heapam_handler.c.

1121 {
1122  HeapScanDesc hscan = (HeapScanDesc) scan;
1123  Page targpage;
1124  OffsetNumber maxoffset;
1125  BufferHeapTupleTableSlot *hslot;
1126 
1127  Assert(TTS_IS_BUFFERTUPLE(slot));
1128 
1129  hslot = (BufferHeapTupleTableSlot *) slot;
1130  targpage = BufferGetPage(hscan->rs_cbuf);
1131  maxoffset = PageGetMaxOffsetNumber(targpage);
1132 
1133  /* Inner loop over all tuples on the selected page */
1134  for (; hscan->rs_cindex <= maxoffset; hscan->rs_cindex++)
1135  {
1136  ItemId itemid;
1137  HeapTuple targtuple = &hslot->base.tupdata;
1138  bool sample_it = false;
1139 
1140  itemid = PageGetItemId(targpage, hscan->rs_cindex);
1141 
1142  /*
1143  * We ignore unused and redirect line pointers. DEAD line pointers
1144  * should be counted as dead, because we need vacuum to run to get rid
1145  * of them. Note that this rule agrees with the way that
1146  * heap_page_prune() counts things.
1147  */
1148  if (!ItemIdIsNormal(itemid))
1149  {
1150  if (ItemIdIsDead(itemid))
1151  *deadrows += 1;
1152  continue;
1153  }
1154 
1155  ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);
1156 
1157  targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
1158  targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
1159  targtuple->t_len = ItemIdGetLength(itemid);
1160 
1161  switch (HeapTupleSatisfiesVacuum(targtuple, OldestXmin,
1162  hscan->rs_cbuf))
1163  {
1164  case HEAPTUPLE_LIVE:
1165  sample_it = true;
1166  *liverows += 1;
1167  break;
1168 
1169  case HEAPTUPLE_DEAD:
1171  /* Count dead and recently-dead rows */
1172  *deadrows += 1;
1173  break;
1174 
1176 
1177  /*
1178  * Insert-in-progress rows are not counted. We assume that
1179  * when the inserting transaction commits or aborts, it will
1180  * send a stats message to increment the proper count. This
1181  * works right only if that transaction ends after we finish
1182  * analyzing the table; if things happen in the other order,
1183  * its stats update will be overwritten by ours. However, the
1184  * error will be large only if the other transaction runs long
1185  * enough to insert many tuples, so assuming it will finish
1186  * after us is the safer option.
1187  *
1188  * A special case is that the inserting transaction might be
1189  * our own. In this case we should count and sample the row,
1190  * to accommodate users who load a table and analyze it in one
1191  * transaction. (pgstat_report_analyze has to adjust the
1192  * numbers we report to the cumulative stats system to make
1193  * this come out right.)
1194  */
1196  {
1197  sample_it = true;
1198  *liverows += 1;
1199  }
1200  break;
1201 
1203 
1204  /*
1205  * We count and sample delete-in-progress rows the same as
1206  * live ones, so that the stats counters come out right if the
1207  * deleting transaction commits after us, per the same
1208  * reasoning given above.
1209  *
1210  * If the delete was done by our own transaction, however, we
1211  * must count the row as dead to make pgstat_report_analyze's
1212  * stats adjustments come out right. (Note: this works out
1213  * properly when the row was both inserted and deleted in our
1214  * xact.)
1215  *
1216  * The net effect of these choices is that we act as though an
1217  * IN_PROGRESS transaction hasn't happened yet, except if it
1218  * is our own transaction, which we assume has happened.
1219  *
1220  * This approach ensures that we behave sanely if we see both
1221  * the pre-image and post-image rows for a row being updated
1222  * by a concurrent transaction: we will sample the pre-image
1223  * but not the post-image. We also get sane results if the
1224  * concurrent transaction never commits.
1225  */
1227  *deadrows += 1;
1228  else
1229  {
1230  sample_it = true;
1231  *liverows += 1;
1232  }
1233  break;
1234 
1235  default:
1236  elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1237  break;
1238  }
1239 
1240  if (sample_it)
1241  {
1242  ExecStoreBufferHeapTuple(targtuple, slot, hscan->rs_cbuf);
1243  hscan->rs_cindex++;
1244 
1245  /* note that we leave the buffer locked here! */
1246  return true;
1247  }
1248  }
1249 
1250  /* Now release the lock and pin on the page */
1251  UnlockReleaseBuffer(hscan->rs_cbuf);
1252  hscan->rs_cbuf = InvalidBuffer;
1253 
1254  /* also prevent old slot contents from having pin on page */
1255  ExecClearTuple(slot);
1256 
1257  return false;
1258 }
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4008
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:351
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:240
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:369
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
#define ItemIdIsNormal(itemId)
Definition: itemid.h:99
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
uint32 t_len
Definition: htup.h:64
Oid t_tableOid
Definition: htup.h:66
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:471

References Assert(), BufferGetPage(), elog(), ERROR, ExecClearTuple(), ExecStoreBufferHeapTuple(), HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleSatisfiesVacuum(), InvalidBuffer, ItemIdGetLength, ItemIdIsDead, ItemIdIsNormal, ItemPointerSet(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), RelationGetRelid, HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_cindex, TableScanDescData::rs_rd, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransactionIdIsCurrentTransactionId(), TTS_IS_BUFFERTUPLE, and UnlockReleaseBuffer().

◆ heapam_scan_bitmap_next_block()

static bool heapam_scan_bitmap_next_block ( TableScanDesc  scan,
TBMIterateResult tbmres 
)
static

Definition at line 2210 of file heapam_handler.c.

2212 {
2213  HeapScanDesc hscan = (HeapScanDesc) scan;
2214  BlockNumber block = tbmres->blockno;
2215  Buffer buffer;
2216  Snapshot snapshot;
2217  int ntup;
2218 
2219  hscan->rs_cindex = 0;
2220  hscan->rs_ntuples = 0;
2221 
2222  /*
2223  * Ignore any claimed entries past what we think is the end of the
2224  * relation. It may have been extended after the start of our scan (we
2225  * only hold an AccessShareLock, and it could be inserts from this
2226  * backend).
2227  */
2228  if (block >= hscan->rs_nblocks)
2229  return false;
2230 
2231  /*
2232  * Acquire pin on the target heap page, trading in any pin we held before.
2233  */
2234  hscan->rs_cbuf = ReleaseAndReadBuffer(hscan->rs_cbuf,
2235  scan->rs_rd,
2236  block);
2237  hscan->rs_cblock = block;
2238  buffer = hscan->rs_cbuf;
2239  snapshot = scan->rs_snapshot;
2240 
2241  ntup = 0;
2242 
2243  /*
2244  * Prune and repair fragmentation for the whole page, if possible.
2245  */
2246  heap_page_prune_opt(scan->rs_rd, buffer);
2247 
2248  /*
2249  * We must hold share lock on the buffer content while examining tuple
2250  * visibility. Afterwards, however, the tuples we have found to be
2251  * visible are guaranteed good as long as we hold the buffer pin.
2252  */
2253  LockBuffer(buffer, BUFFER_LOCK_SHARE);
2254 
2255  /*
2256  * We need two separate strategies for lossy and non-lossy cases.
2257  */
2258  if (tbmres->ntuples >= 0)
2259  {
2260  /*
2261  * Bitmap is non-lossy, so we just look through the offsets listed in
2262  * tbmres; but we have to follow any HOT chain starting at each such
2263  * offset.
2264  */
2265  int curslot;
2266 
2267  for (curslot = 0; curslot < tbmres->ntuples; curslot++)
2268  {
2269  OffsetNumber offnum = tbmres->offsets[curslot];
2270  ItemPointerData tid;
2271  HeapTupleData heapTuple;
2272 
2273  ItemPointerSet(&tid, block, offnum);
2274  if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
2275  &heapTuple, NULL, true))
2276  hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
2277  }
2278  }
2279  else
2280  {
2281  /*
2282  * Bitmap is lossy, so we must examine each line pointer on the page.
2283  * But we can ignore HOT chains, since we'll check each tuple anyway.
2284  */
2285  Page page = BufferGetPage(buffer);
2286  OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
2287  OffsetNumber offnum;
2288 
2289  for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
2290  {
2291  ItemId lp;
2292  HeapTupleData loctup;
2293  bool valid;
2294 
2295  lp = PageGetItemId(page, offnum);
2296  if (!ItemIdIsNormal(lp))
2297  continue;
2298  loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2299  loctup.t_len = ItemIdGetLength(lp);
2300  loctup.t_tableOid = scan->rs_rd->rd_id;
2301  ItemPointerSet(&loctup.t_self, block, offnum);
2302  valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
2303  if (valid)
2304  {
2305  hscan->rs_vistuples[ntup++] = offnum;
2306  PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
2307  HeapTupleHeaderGetXmin(loctup.t_data));
2308  }
2309  HeapCheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
2310  buffer, snapshot);
2311  }
2312  }
2313 
2314  LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
2315 
2316  Assert(ntup <= MaxHeapTuplesPerPage);
2317  hscan->rs_ntuples = ntup;
2318 
2319  return ntup > 0;
2320 }
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition: heapam.c:10083
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
Definition: predicate.c:2555
int rs_ntuples
Definition: heapam.h:77
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]
Definition: heapam.h:78
Oid rd_id
Definition: rel.h:112
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
Definition: tidbitmap.h:46
BlockNumber blockno
Definition: tidbitmap.h:42

References Assert(), TBMIterateResult::blockno, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), FirstOffsetNumber, heap_hot_search_buffer(), heap_page_prune_opt(), HeapCheckForSerializableConflictOut(), HeapTupleHeaderGetXmin, HeapTupleSatisfiesVisibility(), ItemIdGetLength, ItemIdIsNormal, ItemPointerGetOffsetNumber(), ItemPointerSet(), LockBuffer(), MaxHeapTuplesPerPage, TBMIterateResult::ntuples, OffsetNumberNext, TBMIterateResult::offsets, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PredicateLockTID(), RelationData::rd_id, ReleaseAndReadBuffer(), HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_cindex, HeapScanDescData::rs_nblocks, HeapScanDescData::rs_ntuples, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, HeapScanDescData::rs_vistuples, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, and HeapTupleData::t_tableOid.

◆ heapam_scan_bitmap_next_tuple()

static bool heapam_scan_bitmap_next_tuple ( TableScanDesc  scan,
TBMIterateResult tbmres,
TupleTableSlot slot 
)
static

Definition at line 2323 of file heapam_handler.c.

2326 {
2327  HeapScanDesc hscan = (HeapScanDesc) scan;
2328  OffsetNumber targoffset;
2329  Page page;
2330  ItemId lp;
2331 
2332  /*
2333  * Out of range? If so, nothing more to look at on this page
2334  */
2335  if (hscan->rs_cindex < 0 || hscan->rs_cindex >= hscan->rs_ntuples)
2336  return false;
2337 
2338  targoffset = hscan->rs_vistuples[hscan->rs_cindex];
2339  page = BufferGetPage(hscan->rs_cbuf);
2340  lp = PageGetItemId(page, targoffset);
2341  Assert(ItemIdIsNormal(lp));
2342 
2343  hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2344  hscan->rs_ctup.t_len = ItemIdGetLength(lp);
2345  hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
2346  ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);
2347 
2349 
2350  /*
2351  * Set up the result slot to point to this tuple. Note that the slot
2352  * acquires a pin on the buffer.
2353  */
2355  slot,
2356  hscan->rs_cbuf);
2357 
2358  hscan->rs_cindex++;
2359 
2360  return true;
2361 }
#define pgstat_count_heap_fetch(rel)
Definition: pgstat.h:610
HeapTupleData rs_ctup
Definition: heapam.h:67

References Assert(), BufferGetPage(), ExecStoreBufferHeapTuple(), ItemIdGetLength, ItemIdIsNormal, ItemPointerSet(), PageGetItem(), PageGetItemId(), pgstat_count_heap_fetch, RelationData::rd_id, HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_cindex, HeapScanDescData::rs_ctup, HeapScanDescData::rs_ntuples, TableScanDescData::rs_rd, HeapScanDescData::rs_vistuples, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, and HeapTupleData::t_tableOid.

◆ heapam_scan_get_blocks_done()

static BlockNumber heapam_scan_get_blocks_done ( HeapScanDesc  hscan)
static

Definition at line 2083 of file heapam_handler.c.

2084 {
2085  ParallelBlockTableScanDesc bpscan = NULL;
2086  BlockNumber startblock;
2087  BlockNumber blocks_done;
2088 
2089  if (hscan->rs_base.rs_parallel != NULL)
2090  {
2092  startblock = bpscan->phs_startblock;
2093  }
2094  else
2095  startblock = hscan->rs_startblock;
2096 
2097  /*
2098  * Might have wrapped around the end of the relation, if startblock was
2099  * not zero.
2100  */
2101  if (hscan->rs_cblock > startblock)
2102  blocks_done = hscan->rs_cblock - startblock;
2103  else
2104  {
2105  BlockNumber nblocks;
2106 
2107  nblocks = bpscan != NULL ? bpscan->phs_nblocks : hscan->rs_nblocks;
2108  blocks_done = nblocks - startblock +
2109  hscan->rs_cblock;
2110  }
2111 
2112  return blocks_done;
2113 }

References ParallelBlockTableScanDescData::phs_nblocks, ParallelBlockTableScanDescData::phs_startblock, HeapScanDescData::rs_base, HeapScanDescData::rs_cblock, HeapScanDescData::rs_nblocks, TableScanDescData::rs_parallel, and HeapScanDescData::rs_startblock.

Referenced by heapam_index_build_range_scan().

◆ heapam_scan_sample_next_block()

static bool heapam_scan_sample_next_block ( TableScanDesc  scan,
SampleScanState scanstate 
)
static

Definition at line 2364 of file heapam_handler.c.

2365 {
2366  HeapScanDesc hscan = (HeapScanDesc) scan;
2367  TsmRoutine *tsm = scanstate->tsmroutine;
2368  BlockNumber blockno;
2369 
2370  /* return false immediately if relation is empty */
2371  if (hscan->rs_nblocks == 0)
2372  return false;
2373 
2374  if (tsm->NextSampleBlock)
2375  {
2376  blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks);
2377  hscan->rs_cblock = blockno;
2378  }
2379  else
2380  {
2381  /* scanning table sequentially */
2382 
2383  if (hscan->rs_cblock == InvalidBlockNumber)
2384  {
2385  Assert(!hscan->rs_inited);
2386  blockno = hscan->rs_startblock;
2387  }
2388  else
2389  {
2390  Assert(hscan->rs_inited);
2391 
2392  blockno = hscan->rs_cblock + 1;
2393 
2394  if (blockno >= hscan->rs_nblocks)
2395  {
2396  /* wrap to beginning of rel, might not have started at 0 */
2397  blockno = 0;
2398  }
2399 
2400  /*
2401  * Report our new scan position for synchronization purposes.
2402  *
2403  * Note: we do this before checking for end of scan so that the
2404  * final state of the position hint is back at the start of the
2405  * rel. That's not strictly necessary, but otherwise when you run
2406  * the same query multiple times the starting position would shift
2407  * a little bit backwards on every invocation, which is confusing.
2408  * We don't guarantee any specific ordering in general, though.
2409  */
2410  if (scan->rs_flags & SO_ALLOW_SYNC)
2411  ss_report_location(scan->rs_rd, blockno);
2412 
2413  if (blockno == hscan->rs_startblock)
2414  {
2415  blockno = InvalidBlockNumber;
2416  }
2417  }
2418  }
2419 
2420  if (!BlockNumberIsValid(blockno))
2421  {
2422  if (BufferIsValid(hscan->rs_cbuf))
2423  ReleaseBuffer(hscan->rs_cbuf);
2424  hscan->rs_cbuf = InvalidBuffer;
2425  hscan->rs_cblock = InvalidBlockNumber;
2426  hscan->rs_inited = false;
2427 
2428  return false;
2429  }
2430 
2431  heapgetpage(scan, blockno);
2432  hscan->rs_inited = true;
2433 
2434  return true;
2435 }
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition: block.h:71
void heapgetpage(TableScanDesc sscan, BlockNumber block)
Definition: heapam.c:377
bool rs_inited
Definition: heapam.h:59
struct TsmRoutine * tsmroutine
Definition: execnodes.h:1487
uint32 rs_flags
Definition: relscan.h:47
NextSampleBlock_function NextSampleBlock
Definition: tsmapi.h:73
void ss_report_location(Relation rel, BlockNumber location)
Definition: syncscan.c:289
@ SO_ALLOW_SYNC
Definition: tableam.h:59

References Assert(), BlockNumberIsValid(), BufferIsValid(), heapgetpage(), InvalidBlockNumber, InvalidBuffer, TsmRoutine::NextSampleBlock, ReleaseBuffer(), HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, TableScanDescData::rs_flags, HeapScanDescData::rs_inited, HeapScanDescData::rs_nblocks, TableScanDescData::rs_rd, HeapScanDescData::rs_startblock, SO_ALLOW_SYNC, ss_report_location(), and SampleScanState::tsmroutine.

◆ heapam_scan_sample_next_tuple()

static bool heapam_scan_sample_next_tuple ( TableScanDesc  scan,
SampleScanState scanstate,
TupleTableSlot slot 
)
static

Definition at line 2438 of file heapam_handler.c.

2440 {
2441  HeapScanDesc hscan = (HeapScanDesc) scan;
2442  TsmRoutine *tsm = scanstate->tsmroutine;
2443  BlockNumber blockno = hscan->rs_cblock;
2444  bool pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
2445 
2446  Page page;
2447  bool all_visible;
2448  OffsetNumber maxoffset;
2449 
2450  /*
2451  * When not using pagemode, we must lock the buffer during tuple
2452  * visibility checks.
2453  */
2454  if (!pagemode)
2456 
2457  page = (Page) BufferGetPage(hscan->rs_cbuf);
2458  all_visible = PageIsAllVisible(page) &&
2460  maxoffset = PageGetMaxOffsetNumber(page);
2461 
2462  for (;;)
2463  {
2464  OffsetNumber tupoffset;
2465 
2467 
2468  /* Ask the tablesample method which tuples to check on this page. */
2469  tupoffset = tsm->NextSampleTuple(scanstate,
2470  blockno,
2471  maxoffset);
2472 
2473  if (OffsetNumberIsValid(tupoffset))
2474  {
2475  ItemId itemid;
2476  bool visible;
2477  HeapTuple tuple = &(hscan->rs_ctup);
2478 
2479  /* Skip invalid tuple pointers. */
2480  itemid = PageGetItemId(page, tupoffset);
2481  if (!ItemIdIsNormal(itemid))
2482  continue;
2483 
2484  tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
2485  tuple->t_len = ItemIdGetLength(itemid);
2486  ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
2487 
2488 
2489  if (all_visible)
2490  visible = true;
2491  else
2492  visible = SampleHeapTupleVisible(scan, hscan->rs_cbuf,
2493  tuple, tupoffset);
2494 
2495  /* in pagemode, heapgetpage did this for us */
2496  if (!pagemode)
2497  HeapCheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
2498  hscan->rs_cbuf, scan->rs_snapshot);
2499 
2500  /* Try next tuple from same page. */
2501  if (!visible)
2502  continue;
2503 
2504  /* Found visible tuple, return it. */
2505  if (!pagemode)
2507 
2508  ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf);
2509 
2510  /* Count successfully-fetched tuples as heap fetches */
2512 
2513  return true;
2514  }
2515  else
2516  {
2517  /*
2518  * If we get here, it means we've exhausted the items on this page
2519  * and it's time to move to the next.
2520  */
2521  if (!pagemode)
2523 
2524  ExecClearTuple(slot);
2525  return false;
2526  }
2527  }
2528 
2529  Assert(0);
2530 }
static bool PageIsAllVisible(Page page)
Definition: bufpage.h:426
static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, OffsetNumber tupoffset)
#define pgstat_count_heap_getnext(rel)
Definition: pgstat.h:605
bool takenDuringRecovery
Definition: snapshot.h:184
NextSampleTuple_function NextSampleTuple
Definition: tsmapi.h:74
@ SO_ALLOW_PAGEMODE
Definition: tableam.h:61

References Assert(), BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), CHECK_FOR_INTERRUPTS, ExecClearTuple(), ExecStoreBufferHeapTuple(), HeapCheckForSerializableConflictOut(), ItemIdGetLength, ItemIdIsNormal, ItemPointerSet(), LockBuffer(), TsmRoutine::NextSampleTuple, OffsetNumberIsValid, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PageIsAllVisible(), pgstat_count_heap_getnext, HeapScanDescData::rs_cblock, HeapScanDescData::rs_cbuf, HeapScanDescData::rs_ctup, TableScanDescData::rs_flags, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, SampleHeapTupleVisible(), SO_ALLOW_PAGEMODE, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, SnapshotData::takenDuringRecovery, and SampleScanState::tsmroutine.

◆ heapam_slot_callbacks()

static const TupleTableSlotOps* heapam_slot_callbacks ( Relation  relation)
static

Definition at line 73 of file heapam_handler.c.

74 {
75  return &TTSOpsBufferHeapTuple;
76 }
const TupleTableSlotOps TTSOpsBufferHeapTuple
Definition: execTuples.c:86

References TTSOpsBufferHeapTuple.

◆ heapam_tuple_complete_speculative()

static void heapam_tuple_complete_speculative ( Relation  relation,
TupleTableSlot slot,
uint32  specToken,
bool  succeeded 
)
static

Definition at line 289 of file heapam_handler.c.

291 {
292  bool shouldFree = true;
293  HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
294 
295  /* adjust the tuple's state accordingly */
296  if (succeeded)
297  heap_finish_speculative(relation, &slot->tts_tid);
298  else
299  heap_abort_speculative(relation, &slot->tts_tid);
300 
301  if (shouldFree)
302  pfree(tuple);
303 }
void heap_finish_speculative(Relation relation, ItemPointer tid)
Definition: heapam.c:5591
void heap_abort_speculative(Relation relation, ItemPointer tid)
Definition: heapam.c:5678
ItemPointerData tts_tid
Definition: tuptable.h:130

References ExecFetchSlotHeapTuple(), heap_abort_speculative(), heap_finish_speculative(), pfree(), and TupleTableSlot::tts_tid.

◆ heapam_tuple_delete()

static TM_Result heapam_tuple_delete ( Relation  relation,
ItemPointer  tid,
CommandId  cid,
Snapshot  snapshot,
Snapshot  crosscheck,
bool  wait,
TM_FailureData tmfd,
bool  changingPart,
LazyTupleTableSlot lockedSlot 
)
static

Definition at line 306 of file heapam_handler.c.

310 {
311  TM_Result result;
312 
313  /*
314  * Currently Deleting of index tuples are handled at vacuum, in case if
315  * the storage itself is cleaning the dead tuples by itself, it is the
316  * time to call the index tuple deletion also.
317  */
318  result = heap_delete(relation, tid, cid, crosscheck, wait,
319  tmfd, changingPart);
320 
321  /*
322  * If the tuple has been concurrently updated, then get the lock on it.
323  * (Do this if caller asked for tat by providing a 'lockedSlot'.) With the
324  * lock held retry of delete should succeed even if there are more
325  * concurrent update attempts.
326  */
327  if (result == TM_Updated && lockedSlot)
328  {
329  TupleTableSlot *evalSlot;
330 
331  Assert(wait);
332 
333  evalSlot = LAZY_TTS_EVAL(lockedSlot);
334  result = heapam_tuple_lock_internal(relation, tid, snapshot,
335  evalSlot, cid, LockTupleExclusive,
338  tmfd, true);
339 
340  if (result == TM_Ok)
341  {
342  tmfd->traversed = true;
343  return TM_Updated;
344  }
345  }
346 
347  return result;
348 }
TM_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart)
Definition: heapam.c:2463
static TM_Result heapam_tuple_lock_internal(Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd, bool updated)
@ LockWaitBlock
Definition: lockoptions.h:39
@ LockTupleExclusive
Definition: lockoptions.h:58
bool traversed
Definition: tableam.h:145
TM_Result
Definition: tableam.h:72
@ TM_Ok
Definition: tableam.h:77
@ TM_Updated
Definition: tableam.h:89
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:260
#define LAZY_TTS_EVAL(lazySlot)
Definition: tuptable.h:334

References Assert(), heap_delete(), heapam_tuple_lock_internal(), LAZY_TTS_EVAL, LockTupleExclusive, LockWaitBlock, TM_Ok, TM_Updated, TM_FailureData::traversed, and TUPLE_LOCK_FLAG_FIND_LAST_VERSION.

◆ heapam_tuple_insert()

static void heapam_tuple_insert ( Relation  relation,
TupleTableSlot slot,
CommandId  cid,
int  options,
BulkInsertState  bistate 
)
static

Definition at line 247 of file heapam_handler.c.

249 {
250  bool shouldFree = true;
251  HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
252 
253  /* Update the tuple with table oid */
254  slot->tts_tableOid = RelationGetRelid(relation);
255  tuple->t_tableOid = slot->tts_tableOid;
256 
257  /* Perform the insertion, and copy the resulting ItemPointer */
258  heap_insert(relation, tuple, cid, options, bistate);
259  ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
260 
261  if (shouldFree)
262  pfree(tuple);
263 }
void heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
Definition: heapam.c:1823
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition: itemptr.h:172

References ExecFetchSlotHeapTuple(), heap_insert(), ItemPointerCopy(), pfree(), RelationGetRelid, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleTableSlot::tts_tableOid, and TupleTableSlot::tts_tid.

◆ heapam_tuple_insert_speculative()

static void heapam_tuple_insert_speculative ( Relation  relation,
TupleTableSlot slot,
CommandId  cid,
int  options,
BulkInsertState  bistate,
uint32  specToken 
)
static

Definition at line 266 of file heapam_handler.c.

269 {
270  bool shouldFree = true;
271  HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
272 
273  /* Update the tuple with table oid */
274  slot->tts_tableOid = RelationGetRelid(relation);
275  tuple->t_tableOid = slot->tts_tableOid;
276 
277  HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
279 
280  /* Perform the insertion, and copy the resulting ItemPointer */
281  heap_insert(relation, tuple, cid, options, bistate);
282  ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
283 
284  if (shouldFree)
285  pfree(tuple);
286 }
#define HEAP_INSERT_SPECULATIVE
Definition: heapam.h:37
#define HeapTupleHeaderSetSpeculativeToken(tup, token)
Definition: htup_details.h:439

References ExecFetchSlotHeapTuple(), heap_insert(), HEAP_INSERT_SPECULATIVE, HeapTupleHeaderSetSpeculativeToken, ItemPointerCopy(), pfree(), RelationGetRelid, HeapTupleData::t_data, HeapTupleData::t_self, HeapTupleData::t_tableOid, TupleTableSlot::tts_tableOid, and TupleTableSlot::tts_tid.

◆ heapam_tuple_lock()

static TM_Result heapam_tuple_lock ( Relation  relation,
ItemPointer  tid,
Snapshot  snapshot,
TupleTableSlot slot,
CommandId  cid,
LockTupleMode  mode,
LockWaitPolicy  wait_policy,
uint8  flags,
TM_FailureData tmfd 
)
static

Definition at line 424 of file heapam_handler.c.

428 {
429  return heapam_tuple_lock_internal(relation, tid, snapshot, slot, cid,
430  mode, wait_policy, flags, tmfd, false);
431 }
static PgChecksumMode mode
Definition: pg_checksums.c:65

References heapam_tuple_lock_internal(), and mode.

◆ heapam_tuple_lock_internal()

static TM_Result heapam_tuple_lock_internal ( Relation  relation,
ItemPointer  tid,
Snapshot  snapshot,
TupleTableSlot slot,
CommandId  cid,
LockTupleMode  mode,
LockWaitPolicy  wait_policy,
uint8  flags,
TM_FailureData tmfd,
bool  updated 
)
static

Definition at line 439 of file heapam_handler.c.

444 {
446  TM_Result result;
447  Buffer buffer = InvalidBuffer;
448  HeapTuple tuple = &bslot->base.tupdata;
449  bool follow_updates;
450 
451  follow_updates = (flags & TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS) != 0;
452  tmfd->traversed = false;
453 
454  Assert(TTS_IS_BUFFERTUPLE(slot));
455 
456 tuple_lock_retry:
457  tuple->t_self = *tid;
458  if (!updated)
459  result = heap_lock_tuple(relation, tuple, cid, mode, wait_policy,
460  follow_updates, &buffer, tmfd);
461  else
462  result = TM_Updated;
463 
464  if (result == TM_Updated &&
466  {
467  if (!updated)
468  {
469  /* Should not encounter speculative tuple on recheck */
471 
472  ReleaseBuffer(buffer);
473  }
474  else
475  {
476  updated = false;
477  }
478 
479  if (!ItemPointerEquals(&tmfd->ctid, &tuple->t_self))
480  {
481  SnapshotData SnapshotDirty;
482  TransactionId priorXmax;
483 
484  /* it was updated, so look at the updated version */
485  *tid = tmfd->ctid;
486  /* updated row should have xmin matching this xmax */
487  priorXmax = tmfd->xmax;
488 
489  /* signal that a tuple later in the chain is getting locked */
490  tmfd->traversed = true;
491 
492  /*
493  * fetch target tuple
494  *
495  * Loop here to deal with updated or busy tuples
496  */
497  InitDirtySnapshot(SnapshotDirty);
498  for (;;)
499  {
501  ereport(ERROR,
503  errmsg("tuple to be locked was already moved to another partition due to concurrent update")));
504 
505  tuple->t_self = *tid;
506  if (heap_fetch(relation, &SnapshotDirty, tuple, &buffer, true))
507  {
508  /*
509  * If xmin isn't what we're expecting, the slot must have
510  * been recycled and reused for an unrelated tuple. This
511  * implies that the latest version of the row was deleted,
512  * so we need do nothing. (Should be safe to examine xmin
513  * without getting buffer's content lock. We assume
514  * reading a TransactionId to be atomic, and Xmin never
515  * changes in an existing tuple, except to invalid or
516  * frozen, and neither of those can match priorXmax.)
517  */
519  priorXmax))
520  {
521  ReleaseBuffer(buffer);
522  return TM_Deleted;
523  }
524 
525  /* otherwise xmin should not be dirty... */
526  if (TransactionIdIsValid(SnapshotDirty.xmin))
527  ereport(ERROR,
529  errmsg_internal("t_xmin %u is uncommitted in tuple (%u,%u) to be updated in table \"%s\"",
530  SnapshotDirty.xmin,
533  RelationGetRelationName(relation))));
534 
535  /*
536  * If tuple is being updated by other transaction then we
537  * have to wait for its commit/abort, or die trying.
538  */
539  if (TransactionIdIsValid(SnapshotDirty.xmax))
540  {
541  ReleaseBuffer(buffer);
542  switch (wait_policy)
543  {
544  case LockWaitBlock:
545  XactLockTableWait(SnapshotDirty.xmax,
546  relation, &tuple->t_self,
548  break;
549  case LockWaitSkip:
550  if (!ConditionalXactLockTableWait(SnapshotDirty.xmax))
551  /* skip instead of waiting */
552  return TM_WouldBlock;
553  break;
554  case LockWaitError:
555  if (!ConditionalXactLockTableWait(SnapshotDirty.xmax))
556  ereport(ERROR,
557  (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
558  errmsg("could not obtain lock on row in relation \"%s\"",
559  RelationGetRelationName(relation))));
560  break;
561  }
562  continue; /* loop back to repeat heap_fetch */
563  }
564 
565  /*
566  * If tuple was inserted by our own transaction, we have
567  * to check cmin against cid: cmin >= current CID means
568  * our command cannot see the tuple, so we should ignore
569  * it. Otherwise heap_lock_tuple() will throw an error,
570  * and so would any later attempt to update or delete the
571  * tuple. (We need not check cmax because
572  * HeapTupleSatisfiesDirty will consider a tuple deleted
573  * by our transaction dead, regardless of cmax.) We just
574  * checked that priorXmax == xmin, so we can test that
575  * variable instead of doing HeapTupleHeaderGetXmin again.
576  */
577  if (TransactionIdIsCurrentTransactionId(priorXmax) &&
578  HeapTupleHeaderGetCmin(tuple->t_data) >= cid)
579  {
580  tmfd->xmax = priorXmax;
581 
582  /*
583  * Cmin is the problematic value, so store that. See
584  * above.
585  */
586  tmfd->cmax = HeapTupleHeaderGetCmin(tuple->t_data);
587  ReleaseBuffer(buffer);
588  return TM_SelfModified;
589  }
590 
591  /*
592  * This is a live tuple, so try to lock it again.
593  */
594  ReleaseBuffer(buffer);
595  goto tuple_lock_retry;
596  }
597 
598  /*
599  * If the referenced slot was actually empty, the latest
600  * version of the row must have been deleted, so we need do
601  * nothing.
602  */
603  if (tuple->t_data == NULL)
604  {
605  Assert(!BufferIsValid(buffer));
606  return TM_Deleted;
607  }
608 
609  /*
610  * As above, if xmin isn't what we're expecting, do nothing.
611  */
613  priorXmax))
614  {
615  ReleaseBuffer(buffer);
616  return TM_Deleted;
617  }
618 
619  /*
620  * If we get here, the tuple was found but failed
621  * SnapshotDirty. Assuming the xmin is either a committed xact
622  * or our own xact (as it certainly should be if we're trying
623  * to modify the tuple), this must mean that the row was
624  * updated or deleted by either a committed xact or our own
625  * xact. If it was deleted, we can ignore it; if it was
626  * updated then chain up to the next version and repeat the
627  * whole process.
628  *
629  * As above, it should be safe to examine xmax and t_ctid
630  * without the buffer content lock, because they can't be
631  * changing. We'd better hold a buffer pin though.
632  */
633  if (ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
634  {
635  /* deleted, so forget about it */
636  ReleaseBuffer(buffer);
637  return TM_Deleted;
638  }
639 
640  /* updated, so look at the updated row */
641  *tid = tuple->t_data->t_ctid;
642  /* updated row should have xmin matching this xmax */
643  priorXmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
644  ReleaseBuffer(buffer);
645  /* loop back to fetch next in chain */
646  }
647  }
648  else
649  {
650  /* tuple was deleted, so give up */
651  return TM_Deleted;
652  }
653  }
654 
655  slot->tts_tableOid = RelationGetRelid(relation);
656  tuple->t_tableOid = slot->tts_tableOid;
657 
658  /* store in slot, transferring existing pin */
659  ExecStorePinnedBufferHeapTuple(tuple, slot, buffer);
660 
661  return result;
662 }
CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup)
Definition: combocid.c:104
int errmsg(const char *fmt,...)
Definition: elog.c:1069
TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, Buffer *buffer, TM_FailureData *tmfd)
Definition: heapam.c:4082
#define HeapTupleHeaderIsSpeculative(tup)
Definition: htup_details.h:428
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
Definition: itemptr.h:197
bool ConditionalXactLockTableWait(TransactionId xid)
Definition: lmgr.c:741
@ XLTW_FetchUpdated
Definition: lmgr.h:33
@ LockWaitSkip
Definition: lockoptions.h:41
@ LockWaitError
Definition: lockoptions.h:43
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition: pgbench.c:76
#define InitDirtySnapshot(snapshotdata)
Definition: snapmgr.h:74
ItemPointerData t_ctid
Definition: htup_details.h:161
TransactionId xmin
Definition: snapshot.h:157
TransactionId xmax
Definition: snapshot.h:158
TransactionId xmax
Definition: tableam.h:143
CommandId cmax
Definition: tableam.h:144
ItemPointerData ctid
Definition: tableam.h:142
@ TM_Deleted
Definition: tableam.h:92
@ TM_WouldBlock
Definition: tableam.h:102
@ TM_SelfModified
Definition: tableam.h:83
#define TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS
Definition: tableam.h:258
#define TransactionIdEquals(id1, id2)
Definition: transam.h:43

References Assert(), BufferIsValid(), TM_FailureData::cmax, ConditionalXactLockTableWait(), TM_FailureData::ctid, ereport, errcode(), ERRCODE_DATA_CORRUPTED, ERRCODE_T_R_SERIALIZATION_FAILURE, errmsg(), errmsg_internal(), ERROR, ExecStorePinnedBufferHeapTuple(), heap_fetch(), heap_lock_tuple(), HeapTupleHeaderGetCmin(), HeapTupleHeaderGetUpdateXid, HeapTupleHeaderGetXmin, HeapTupleHeaderIsSpeculative, InitDirtySnapshot, InvalidBuffer, ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerIndicatesMovedPartitions(), LockWaitBlock, LockWaitError, LockWaitSkip, mode, RelationGetRelationName, RelationGetRelid, ReleaseBuffer(), HeapTupleHeaderData::t_ctid, HeapTupleData::t_data, HeapTupleData::t_self, HeapTupleData::t_tableOid, TM_Deleted, TM_SelfModified, TM_Updated, TM_WouldBlock, TransactionIdEquals, TransactionIdIsCurrentTransactionId(), TransactionIdIsValid, TM_FailureData::traversed, TTS_IS_BUFFERTUPLE, TupleTableSlot::tts_tableOid, TUPLE_LOCK_FLAG_FIND_LAST_VERSION, TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS, XactLockTableWait(), XLTW_FetchUpdated, TM_FailureData::xmax, SnapshotData::xmax, and SnapshotData::xmin.

Referenced by heapam_tuple_delete(), heapam_tuple_lock(), and heapam_tuple_update().

◆ heapam_tuple_satisfies_snapshot()

static bool heapam_tuple_satisfies_snapshot ( Relation  rel,
TupleTableSlot slot,
Snapshot  snapshot 
)
static

Definition at line 219 of file heapam_handler.c.

221 {
223  bool res;
224 
225  Assert(TTS_IS_BUFFERTUPLE(slot));
226  Assert(BufferIsValid(bslot->buffer));
227 
228  /*
229  * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
230  * Caller should be holding pin, but not lock.
231  */
233  res = HeapTupleSatisfiesVisibility(bslot->base.tuple, snapshot,
234  bslot->buffer);
236 
237  return res;
238 }

References Assert(), BufferHeapTupleTableSlot::buffer, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferIsValid(), HeapTupleSatisfiesVisibility(), LockBuffer(), res, and TTS_IS_BUFFERTUPLE.

◆ heapam_tuple_tid_valid()

static bool heapam_tuple_tid_valid ( TableScanDesc  scan,
ItemPointer  tid 
)
static

Definition at line 210 of file heapam_handler.c.

211 {
212  HeapScanDesc hscan = (HeapScanDesc) scan;
213 
214  return ItemPointerIsValid(tid) &&
216 }
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83

References ItemPointerGetBlockNumber(), ItemPointerIsValid(), and HeapScanDescData::rs_nblocks.

◆ heapam_tuple_update()

static TM_Result heapam_tuple_update ( Relation  relation,
ItemPointer  otid,
TupleTableSlot slot,
CommandId  cid,
Snapshot  snapshot,
Snapshot  crosscheck,
bool  wait,
TM_FailureData tmfd,
LockTupleMode lockmode,
TU_UpdateIndexes update_indexes,
LazyTupleTableSlot lockedSlot 
)
static

Definition at line 352 of file heapam_handler.c.

357 {
358  bool shouldFree = true;
359  HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
360  TM_Result result;
361 
362  /* Update the tuple with table oid */
363  slot->tts_tableOid = RelationGetRelid(relation);
364  tuple->t_tableOid = slot->tts_tableOid;
365 
366  result = heap_update(relation, otid, tuple, cid, crosscheck, wait,
367  tmfd, lockmode, update_indexes);
368  ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
369 
370  /*
371  * Decide whether new index entries are needed for the tuple
372  *
373  * Note: heap_update returns the tid (location) of the new tuple in the
374  * t_self field.
375  *
376  * If the update is not HOT, we must update all indexes. If the update
377  * is HOT, it could be that we updated summarized columns, so we either
378  * update only summarized indexes, or none at all.
379  */
380  if (result != TM_Ok)
381  {
382  Assert(*update_indexes == TU_None);
383  *update_indexes = TU_None;
384  }
385  else if (!HeapTupleIsHeapOnly(tuple))
386  Assert(*update_indexes == TU_All);
387  else
388  Assert((*update_indexes == TU_Summarizing) ||
389  (*update_indexes == TU_None));
390 
391  if (shouldFree)
392  pfree(tuple);
393 
394  /*
395  * If the tuple has been concurrently updated, then get the lock on it.
396  * (Do this if caller asked for tat by providing a 'lockedSlot'.) With the
397  * lock held retry of update should succeed even if there are more
398  * concurrent update attempts.
399  */
400  if (result == TM_Updated && lockedSlot)
401  {
402  TupleTableSlot *evalSlot;
403 
404  Assert(wait);
405 
406  evalSlot = LAZY_TTS_EVAL(lockedSlot);
407  result = heapam_tuple_lock_internal(relation, otid, snapshot,
408  evalSlot, cid, *lockmode,
411  tmfd, true);
412 
413  if (result == TM_Ok)
414  {
415  tmfd->traversed = true;
416  return TM_Updated;
417  }
418  }
419 
420  return result;
421 }
TM_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
Definition: heapam.c:2925
@ TU_Summarizing
Definition: tableam.h:118
@ TU_All
Definition: tableam.h:115
@ TU_None
Definition: tableam.h:112

References Assert(), ExecFetchSlotHeapTuple(), heap_update(), heapam_tuple_lock_internal(), HeapTupleIsHeapOnly, ItemPointerCopy(), LAZY_TTS_EVAL, LockWaitBlock, pfree(), RelationGetRelid, HeapTupleData::t_self, HeapTupleData::t_tableOid, TM_Ok, TM_Updated, TM_FailureData::traversed, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_tid, TU_All, TU_None, TU_Summarizing, and TUPLE_LOCK_FLAG_FIND_LAST_VERSION.

◆ reform_and_rewrite_tuple()

static void reform_and_rewrite_tuple ( HeapTuple  tuple,
Relation  OldHeap,
Relation  NewHeap,
Datum values,
bool isnull,
RewriteState  rwstate 
)
static

Definition at line 2555 of file heapam_handler.c.

2558 {
2559  TupleDesc oldTupDesc = RelationGetDescr(OldHeap);
2560  TupleDesc newTupDesc = RelationGetDescr(NewHeap);
2561  HeapTuple copiedTuple;
2562  int i;
2563 
2564  heap_deform_tuple(tuple, oldTupDesc, values, isnull);
2565 
2566  /* Be sure to null out any dropped columns */
2567  for (i = 0; i < newTupDesc->natts; i++)
2568  {
2569  if (TupleDescAttr(newTupDesc, i)->attisdropped)
2570  isnull[i] = true;
2571  }
2572 
2573  copiedTuple = heap_form_tuple(newTupDesc, values, isnull);
2574 
2575  /* The heap rewrite module does the rest */
2576  rewrite_heap_tuple(rwstate, tuple, copiedTuple);
2577 
2578  heap_freetuple(copiedTuple);
2579 }
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:1249
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
void rewrite_heap_tuple(RewriteState state, HeapTuple old_tuple, HeapTuple new_tuple)
Definition: rewriteheap.c:360

References heap_deform_tuple(), heap_form_tuple(), heap_freetuple(), i, TupleDescData::natts, RelationGetDescr, rewrite_heap_tuple(), TupleDescAttr, and values.

Referenced by heapam_relation_copy_for_cluster().

◆ SampleHeapTupleVisible()

static bool SampleHeapTupleVisible ( TableScanDesc  scan,
Buffer  buffer,
HeapTuple  tuple,
OffsetNumber  tupoffset 
)
static

Definition at line 2585 of file heapam_handler.c.

2588 {
2589  HeapScanDesc hscan = (HeapScanDesc) scan;
2590 
2591  if (scan->rs_flags & SO_ALLOW_PAGEMODE)
2592  {
2593  /*
2594  * In pageatatime mode, heapgetpage() already did visibility checks,
2595  * so just look at the info it left in rs_vistuples[].
2596  *
2597  * We use a binary search over the known-sorted array. Note: we could
2598  * save some effort if we insisted that NextSampleTuple select tuples
2599  * in increasing order, but it's not clear that there would be enough
2600  * gain to justify the restriction.
2601  */
2602  int start = 0,
2603  end = hscan->rs_ntuples - 1;
2604 
2605  while (start <= end)
2606  {
2607  int mid = (start + end) / 2;
2608  OffsetNumber curoffset = hscan->rs_vistuples[mid];
2609 
2610  if (tupoffset == curoffset)
2611  return true;
2612  else if (tupoffset < curoffset)
2613  end = mid - 1;
2614  else
2615  start = mid + 1;
2616  }
2617 
2618  return false;
2619  }
2620  else
2621  {
2622  /* Otherwise, we have to check the tuple individually. */
2623  return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
2624  buffer);
2625  }
2626 }

References HeapTupleSatisfiesVisibility(), TableScanDescData::rs_flags, HeapScanDescData::rs_ntuples, TableScanDescData::rs_snapshot, HeapScanDescData::rs_vistuples, and SO_ALLOW_PAGEMODE.

Referenced by heapam_scan_sample_next_tuple().

Variable Documentation

◆ heapam_methods

static const TableAmRoutine heapam_methods
static

Definition at line 64 of file heapam_handler.c.

Referenced by GetHeapamTableAmRoutine(), and heap_tableam_handler().