1/*-------------------------------------------------------------------------
4 * heap table access method code
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/heap/heapam_handler.c
15 * This files wires up the lower level heapam.c et al routines with the
16 * tableam abstraction.
18 *-------------------------------------------------------------------------
64/* ------------------------------------------------------------------------
65 * Slot related callbacks for heap AM
66 * ------------------------------------------------------------------------
76/* ------------------------------------------------------------------------
77 * Index Scan Callbacks for heap AM
78 * ------------------------------------------------------------------------
119 bool *call_again,
bool *all_dead)
127 /* We can skip the buffer-switching logic if we're in mid-HOT chain. */
130 /* Switch to correct buffer if we don't have it already */
138 * Prune page, but only if we weren't already on this page
140 if (prev_buf != hscan->
xs_cbuf)
144 /* Obtain share-lock on the buffer so we can examine visibility */
150 &bslot->base.tupdata,
153 bslot->base.tupdata.t_self = *tid;
159 * Only in a non-MVCC snapshot can more than one member of the HOT
169 /* We've reached the end of the HOT chain. */
173 return got_heap_tuple;
177/* ------------------------------------------------------------------------
178 * Callbacks for non-modifying operations on individual tuples for heap AM
179 * ------------------------------------------------------------------------
193 bslot->base.tupdata.t_self = *tid;
194 if (
heap_fetch(relation, snapshot, &bslot->base.tupdata, &buffer,
false))
196 /* store in slot, transferring existing pin */
226 * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
227 * Caller should be holding pin, but not lock.
238/* ----------------------------------------------------------------------------
239 * Functions for manipulations of physical tuples for heap AM.
240 * ----------------------------------------------------------------------------
247 bool shouldFree =
true;
250 /* Update the tuple with table oid */
254 /* Perform the insertion, and copy the resulting ItemPointer */
267 bool shouldFree =
true;
270 /* Update the tuple with table oid */
277 /* Perform the insertion, and copy the resulting ItemPointer */
287 uint32 specToken,
bool succeeded)
289 bool shouldFree =
true;
292 /* adjust the tuple's state accordingly */
308 * Currently Deleting of index tuples are handled at vacuum, in case if
309 * the storage itself is cleaning the dead tuples by itself, it is the
310 * time to call the index tuple deletion also.
312 return heap_delete(relation, tid, cid, crosscheck, wait, tmfd, changingPart);
322 bool shouldFree =
true;
326 /* Update the tuple with table oid */
330 result =
heap_update(relation, otid, tuple, cid, crosscheck, wait,
331 tmfd, lockmode, update_indexes);
335 * Decide whether new index entries are needed for the tuple
337 * Note: heap_update returns the tid (location) of the new tuple in the
340 * If the update is not HOT, we must update all indexes. If the update is
341 * HOT, it could be that we updated summarized columns, so we either
342 * update only summarized indexes, or none at all.
381 follow_updates, &buffer, tmfd);
386 /* Should not encounter speculative tuple on recheck */
396 /* it was updated, so look at the updated version */
398 /* updated row should have xmin matching this xmax */
399 priorXmax = tmfd->
xmax;
401 /* signal that a tuple later in the chain is getting locked */
407 * Loop here to deal with updated or busy tuples
415 errmsg(
"tuple to be locked was already moved to another partition due to concurrent update")));
418 if (
heap_fetch(relation, &SnapshotDirty, tuple, &buffer,
true))
421 * If xmin isn't what we're expecting, the slot must have
422 * been recycled and reused for an unrelated tuple. This
423 * implies that the latest version of the row was deleted,
424 * so we need do nothing. (Should be safe to examine xmin
425 * without getting buffer's content lock. We assume
426 * reading a TransactionId to be atomic, and Xmin never
427 * changes in an existing tuple, except to invalid or
428 * frozen, and neither of those can match priorXmax.)
437 /* otherwise xmin should not be dirty... */
441 errmsg_internal(
"t_xmin %u is uncommitted in tuple (%u,%u) to be updated in table \"%s\"",
448 * If tuple is being updated by other transaction then we
449 * have to wait for its commit/abort, or die trying.
463 /* skip instead of waiting */
469 (
errcode(ERRCODE_LOCK_NOT_AVAILABLE),
470 errmsg(
"could not obtain lock on row in relation \"%s\"",
474 continue;
/* loop back to repeat heap_fetch */
478 * If tuple was inserted by our own transaction, we have
479 * to check cmin against cid: cmin >= current CID means
480 * our command cannot see the tuple, so we should ignore
481 * it. Otherwise heap_lock_tuple() will throw an error,
482 * and so would any later attempt to update or delete the
483 * tuple. (We need not check cmax because
484 * HeapTupleSatisfiesDirty will consider a tuple deleted
485 * by our transaction dead, regardless of cmax.) We just
486 * checked that priorXmax == xmin, so we can test that
487 * variable instead of doing HeapTupleHeaderGetXmin again.
492 tmfd->
xmax = priorXmax;
495 * Cmin is the problematic value, so store that. See
504 * This is a live tuple, so try to lock it again.
507 goto tuple_lock_retry;
511 * If the referenced slot was actually empty, the latest
512 * version of the row must have been deleted, so we need do
515 if (tuple->
t_data == NULL)
522 * As above, if xmin isn't what we're expecting, do nothing.
532 * If we get here, the tuple was found but failed
533 * SnapshotDirty. Assuming the xmin is either a committed xact
534 * or our own xact (as it certainly should be if we're trying
535 * to modify the tuple), this must mean that the row was
536 * updated or deleted by either a committed xact or our own
537 * xact. If it was deleted, we can ignore it; if it was
538 * updated then chain up to the next version and repeat the
541 * As above, it should be safe to examine xmax and t_ctid
542 * without the buffer content lock, because they can't be
543 * changing. We'd better hold a buffer pin though.
547 /* deleted, so forget about it */
552 /* updated, so look at the updated row */
554 /* updated row should have xmin matching this xmax */
557 /* loop back to fetch next in chain */
562 /* tuple was deleted, so give up */
570 /* store in slot, transferring existing pin */
577/* ------------------------------------------------------------------------
578 * DDL related callbacks for heap AM.
579 * ------------------------------------------------------------------------
592 * Initialize to the minimum XID that could put tuples in the table. We
593 * know that no xacts older than RecentXmin are still running, so that
599 * Similarly, initialize the minimum Multixact to the first value that
600 * could possibly be stored in tuples in the table. Running transactions
601 * could reuse values from their local cache, so we are careful to
602 * consider all currently running multis.
604 * XXX this could be refined further, but is it worth the hassle?
611 * If required, set up an init fork for an unlogged table so that it can
612 * be correctly reinitialized on restart.
614 if (persistence == RELPERSISTENCE_UNLOGGED)
617 rel->
rd_rel->relkind == RELKIND_TOASTVALUE);
637 * Since we copy the file directly without looking at the shared buffers,
638 * we'd better first flush out any pages of the source relation that are
639 * in shared buffers. We assume no new changes will be made while we are
640 * holding exclusive lock on the rel.
645 * Create and copy all forks of the relation, and schedule unlinking of
646 * old physical files.
648 * NOTE: any conflict in relfilenumber value will be caught in
649 * RelationCreateStorage().
655 rel->
rd_rel->relpersistence);
657 /* copy those extra forks that exist */
666 * WAL log creation if the relation is persistent, or this is the
667 * init fork of an unlogged relation.
670 (rel->
rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
674 rel->
rd_rel->relpersistence);
679 /* drop old relation, and close new one */
691 double *tups_vacuumed,
692 double *tups_recently_dead)
698 bool is_system_catalog;
709 /* Remember if it's a system catalog */
713 * Valid smgr_targblock implies something already wrote to the relation.
714 * This may be harmless, but this function hasn't planned for it.
718 /* Preallocate values/isnull arrays */
719 natts = newTupDesc->
natts;
721 isnull = (
bool *)
palloc(natts *
sizeof(
bool));
723 /* Initialize the rewrite operation */
728 /* Set up sorting if wanted */
737 * Prepare to scan the OldHeap. To ensure we see recently-dead tuples
738 * that still need to be copied, we scan with SnapshotAny and use
739 * HeapTupleSatisfiesVacuum for the visibility test.
741 if (OldIndex != NULL && !use_sort)
743 const int ci_index[] = {
749 /* Set phase and OIDOldIndex to columns */
761 /* In scan-and-sort mode and also VACUUM FULL, set phase */
769 /* Set total heap blocks */
778 * Scan through the OldHeap, either in OldIndex order or sequentially;
779 * copy each tuple into the NewHeap, or transiently to the tuplesort
780 * module. Note that we don't bother sorting dead tuples (they won't get
781 * to the new table anyway).
791 if (indexScan != NULL)
796 /* Since we used no scan keys, should never need to recheck */
798 elog(
ERROR,
"CLUSTER does not support lossy index conditions");
805 * If the last pages of the scan were empty, we would go to
806 * the next phase while heap_blks_scanned != heap_blks_total.
807 * Instead, to ensure that heap_blks_scanned is equivalent to
808 * heap_blks_total after the table scan phase, this parameter
809 * is manually updated to the correct value when the table
818 * In scan-and-sort mode and also VACUUM FULL, set heap blocks
821 * Note that heapScan may start at an offset and wrap around, i.e.
822 * rs_startblock may be >0, and rs_cblock may end with a number
823 * below rs_startblock. To prevent showing this wraparound to the
824 * user, we offset rs_cblock by rs_startblock (modulo rs_nblocks).
845 /* Definitely dead */
849 *tups_recently_dead += 1;
852 /* Live or recently dead, must copy it */
858 * Since we hold exclusive lock on the relation, normally the
859 * only way to see this is if it was inserted earlier in our
860 * own transaction. However, it can happen in system
861 * catalogs, since we tend to release write lock before commit
862 * there. Give a warning if neither case applies; but in any
863 * case we had better copy it.
865 if (!is_system_catalog &&
867 elog(
WARNING,
"concurrent insert in progress within table \"%s\"",
875 * Similar situation to INSERT_IN_PROGRESS case.
877 if (!is_system_catalog &&
879 elog(
WARNING,
"concurrent delete in progress within table \"%s\"",
881 /* treat as recently dead */
882 *tups_recently_dead += 1;
886 elog(
ERROR,
"unexpected HeapTupleSatisfiesVacuum result");
887 isdead =
false;
/* keep compiler quiet */
896 /* heap rewrite module still needs to see it... */
899 /* A previous recently-dead tuple is now known dead */
901 *tups_recently_dead -= 1;
907 if (tuplesort != NULL)
912 * In scan-and-sort mode, report increase in number of tuples
920 const int ct_index[] = {
930 * In indexscan mode and also VACUUM FULL, report increase in
931 * number of tuples scanned and written
933 ct_val[0] = *num_tuples;
934 ct_val[1] = *num_tuples;
939 if (indexScan != NULL)
941 if (tableScan != NULL)
947 * In scan-and-sort mode, complete the sort, then read out all live tuples
948 * from the tuplestore and write them to the new relation.
950 if (tuplesort != NULL)
954 /* Report that we are now sorting tuples */
960 /* Report that we are now writing new heap */
979 /* Report n_tuples */
987 /* Write out any remaining tuples, and fsync if needed */
996 * Prepare to analyze the next block in the read stream. Returns false if
997 * the stream is exhausted and true otherwise. The scan must have been started
998 * with SO_TYPE_ANALYZE option.
1000 * This routine holds a buffer pin and lock on the heap page. They are held
1001 * until heapam_scan_analyze_next_tuple() returns false. That is until all the
1002 * items of the heap page are analyzed.
1010 * We must maintain a pin on the target page's buffer to ensure that
1011 * concurrent activity - e.g. HOT pruning - doesn't delete tuples out from
1012 * under us. It comes from the stream already pinned. We also choose to
1013 * hold sharelock on the buffer throughout --- we could release and
1014 * re-acquire sharelock for each tuple, but since we aren't doing much
1015 * work per tuple, the extra lock traffic is probably better avoided.
1030 double *liverows,
double *deadrows,
1044 /* Inner loop over all tuples on the selected page */
1048 HeapTuple targtuple = &hslot->base.tupdata;
1049 bool sample_it =
false;
1054 * We ignore unused and redirect line pointers. DEAD line pointers
1055 * should be counted as dead, because we need vacuum to run to get rid
1056 * of them. Note that this rule agrees with the way that
1057 * heap_page_prune_and_freeze() counts things.
1082 /* Count dead and recently-dead rows */
1089 * Insert-in-progress rows are not counted. We assume that
1090 * when the inserting transaction commits or aborts, it will
1091 * send a stats message to increment the proper count. This
1092 * works right only if that transaction ends after we finish
1093 * analyzing the table; if things happen in the other order,
1094 * its stats update will be overwritten by ours. However, the
1095 * error will be large only if the other transaction runs long
1096 * enough to insert many tuples, so assuming it will finish
1097 * after us is the safer option.
1099 * A special case is that the inserting transaction might be
1100 * our own. In this case we should count and sample the row,
1101 * to accommodate users who load a table and analyze it in one
1102 * transaction. (pgstat_report_analyze has to adjust the
1103 * numbers we report to the cumulative stats system to make
1104 * this come out right.)
1116 * We count and sample delete-in-progress rows the same as
1117 * live ones, so that the stats counters come out right if the
1118 * deleting transaction commits after us, per the same
1119 * reasoning given above.
1121 * If the delete was done by our own transaction, however, we
1122 * must count the row as dead to make pgstat_report_analyze's
1123 * stats adjustments come out right. (Note: this works out
1124 * properly when the row was both inserted and deleted in our
1127 * The net effect of these choices is that we act as though an
1128 * IN_PROGRESS transaction hasn't happened yet, except if it
1129 * is our own transaction, which we assume has happened.
1131 * This approach ensures that we behave sanely if we see both
1132 * the pre-image and post-image rows for a row being updated
1133 * by a concurrent transaction: we will sample the pre-image
1134 * but not the post-image. We also get sane results if the
1135 * concurrent transaction never commits.
1147 elog(
ERROR,
"unexpected HeapTupleSatisfiesVacuum result");
1156 /* note that we leave the buffer locked here! */
1161 /* Now release the lock and pin on the page */
1165 /* also prevent old slot contents from having pin on page */
1181 void *callback_state,
1185 bool is_system_catalog;
1186 bool checking_uniqueness;
1196 bool need_unregister_snapshot =
false;
1207 /* Remember if it's a system catalog */
1210 /* See whether we're verifying uniqueness/exclusion properties */
1211 checking_uniqueness = (indexInfo->
ii_Unique ||
1215 * "Any visible" mode is not compatible with uniqueness checks; make sure
1216 * only one of those is requested.
1218 Assert(!(anyvisible && checking_uniqueness));
1221 * Need an EState for evaluation of index expressions and partial-index
1222 * predicates. Also a slot to hold the current tuple.
1228 /* Arrange for econtext's scan tuple to be the tuple under test */
1231 /* Set up execution state for predicate, if any. */
1235 * Prepare for scan of the base relation. In a normal index build, we use
1236 * SnapshotAny because we must retrieve all tuples and do our own time
1237 * qual checks (because we have to index RECENTLY_DEAD tuples). In a
1238 * concurrent build, or during bootstrap, we take a regular MVCC snapshot
1239 * and index whatever's live according to that.
1243 /* okay to ignore lazy VACUUMs here */
1250 * Serial index build.
1252 * Must begin our own heap scan in this case. We may also need to
1253 * register a snapshot whose lifetime is under our direct control.
1258 need_unregister_snapshot =
true;
1264 snapshot,
/* snapshot */
1265 0,
/* number of keys */
1266 NULL,
/* scan key */
1267 true,
/* buffer access strategy OK */
1268 allow_sync);
/* syncscan OK? */
1273 * Parallel index build.
1275 * Parallel case never registers/unregisters own snapshot. Snapshot
1276 * is taken from parallel heap scan, and is SnapshotAny or an MVCC
1277 * snapshot, based on same criteria as serial case.
1287 * Must have called GetOldestNonRemovableTransactionId() if using
1288 * SnapshotAny. Shouldn't have for an MVCC snapshot. (It's especially
1289 * worth checking this for parallel builds, since ambuild routines that
1290 * support parallel builds must work these details out for themselves.)
1297 /* Publish number of blocks to scan */
1316 /* set our scan endpoints */
1321 /* syncscan can only be requested on whole relation */
1322 Assert(start_blockno == 0);
1329 * Scan all tuples in the base relation.
1337 /* Report scan progress, if asked to. */
1342 if (blocks_done != previous_blkno)
1346 previous_blkno = blocks_done;
1351 * When dealing with a HOT-chain of updated tuples, we want to index
1352 * the values of the live tuple (if any), but index it under the TID
1353 * of the chain's root tuple. This approach is necessary to preserve
1354 * the HOT-chain structure in the heap. So we need to be able to find
1355 * the root item offset for every tuple that's in a HOT-chain. When
1356 * first reaching a new page of the relation, call
1357 * heap_get_root_tuples() to build a map of root item offsets on the
1360 * It might look unsafe to use this information across buffer
1361 * lock/unlock. However, we hold ShareLock on the table so no
1362 * ordinary insert/update/delete should occur; and we hold pin on the
1363 * buffer continuously while visiting the page, so no pruning
1364 * operation can occur either.
1366 * In cases with only ShareUpdateExclusiveLock on the table, it's
1367 * possible for some HOT tuples to appear that we didn't know about
1368 * when we first read the page. To handle that case, we re-obtain the
1369 * list of root offsets when a HOT tuple points to a root item that we
1372 * Also, although our opinions about tuple liveness could change while
1373 * we scan the page (due to concurrent transaction commits/aborts),
1374 * the chain root locations won't, so this info doesn't need to be
1375 * rebuilt after waiting for another transaction.
1377 * Note the implied assumption that there is no more than one live
1378 * tuple per HOT-chain --- else we could create more than one index
1379 * entry pointing to the same root tuple.
1394 /* do our own time qual check */
1401 * We could possibly get away with not locking the buffer here,
1402 * since caller should hold ShareLock on the relation, but let's
1403 * be conservative about it. (This remark is still correct even
1404 * with HOT-pruning: our pin on the buffer prevents pruning.)
1409 * The criteria for counting a tuple as live in this block need to
1410 * match what analyze.c's heapam_scan_analyze_next_tuple() does,
1411 * otherwise CREATE INDEX and ANALYZE may produce wildly different
1412 * reltuples values, e.g. when there are many recently-dead
1419 /* Definitely dead, we can ignore it */
1421 tupleIsAlive =
false;
1424 /* Normal case, index and unique-check it */
1426 tupleIsAlive =
true;
1427 /* Count it as live, too */
1433 * If tuple is recently deleted then we must index it
1434 * anyway to preserve MVCC semantics. (Pre-existing
1435 * transactions could try to use the index after we finish
1436 * building it, and may need to see such tuples.)
1438 * However, if it was HOT-updated then we must only index
1439 * the live tuple at the end of the HOT-chain. Since this
1440 * breaks semantics for pre-existing snapshots, mark the
1441 * index as unusable for them.
1443 * We don't count recently-dead tuples in reltuples, even
1444 * if we index them; see heapam_scan_analyze_next_tuple().
1449 /* mark the index as unsafe for old snapshots */
1454 /* In any case, exclude the tuple from unique-checking */
1455 tupleIsAlive =
false;
1460 * In "anyvisible" mode, this tuple is visible and we
1461 * don't need any further checks.
1466 tupleIsAlive =
true;
1472 * Since caller should hold ShareLock or better, normally
1473 * the only way to see this is if it was inserted earlier
1474 * in our own transaction. However, it can happen in
1475 * system catalogs, since we tend to release write lock
1476 * before commit there. Give a warning if neither case
1482 if (!is_system_catalog)
1483 elog(
WARNING,
"concurrent insert in progress within table \"%s\"",
1487 * If we are performing uniqueness checks, indexing
1488 * such a tuple could lead to a bogus uniqueness
1489 * failure. In that case we wait for the inserting
1490 * transaction to finish and check again.
1492 if (checking_uniqueness)
1495 * Must drop the lock on the buffer before we wait
1508 * For consistency with
1509 * heapam_scan_analyze_next_tuple(), count
1510 * HEAPTUPLE_INSERT_IN_PROGRESS tuples as live only
1511 * when inserted by our own transaction.
1517 * We must index such tuples, since if the index build
1518 * commits then they're good.
1521 tupleIsAlive =
true;
1526 * As with INSERT_IN_PROGRESS case, this is unexpected
1527 * unless it's our own deletion or a system catalog; but
1528 * in anyvisible mode, this tuple is visible.
1533 tupleIsAlive =
false;
1541 if (!is_system_catalog)
1542 elog(
WARNING,
"concurrent delete in progress within table \"%s\"",
1546 * If we are performing uniqueness checks, assuming
1547 * the tuple is dead could lead to missing a
1548 * uniqueness violation. In that case we wait for the
1549 * deleting transaction to finish and check again.
1551 * Also, if it's a HOT-updated tuple, we should not
1552 * index it but rather the live tuple at the end of
1553 * the HOT-chain. However, the deleting transaction
1554 * could abort, possibly leaving this tuple as live
1555 * after all, in which case it has to be indexed. The
1556 * only way to know what to do is to wait for the
1557 * deleting transaction to finish and check again.
1559 if (checking_uniqueness ||
1563 * Must drop the lock on the buffer before we wait
1574 * Otherwise index it but don't check for uniqueness,
1575 * the same as a RECENTLY_DEAD tuple.
1580 * Count HEAPTUPLE_DELETE_IN_PROGRESS tuples as live,
1581 * if they were not deleted by the current
1582 * transaction. That's what
1583 * heapam_scan_analyze_next_tuple() does, and we want
1584 * the behavior to be consistent.
1591 * It's a HOT-updated tuple deleted by our own xact.
1592 * We can assume the deletion will commit (else the
1593 * index contents don't matter), so treat the same as
1594 * RECENTLY_DEAD HOT-updated tuples.
1597 /* mark the index as unsafe for old snapshots */
1603 * It's a regular tuple deleted by our own xact. Index
1604 * it, but don't check for uniqueness nor count in
1605 * reltuples, the same as a RECENTLY_DEAD tuple.
1609 /* In any case, exclude the tuple from unique-checking */
1610 tupleIsAlive =
false;
1613 elog(
ERROR,
"unexpected HeapTupleSatisfiesVacuum result");
1614 indexIt = tupleIsAlive =
false;
/* keep compiler quiet */
1625 /* heap_getnext did the time qual check */
1626 tupleIsAlive =
true;
1632 /* Set up for predicate or expression evaluation */
1636 * In a partial index, discard tuples that don't satisfy the
1639 if (predicate != NULL)
1641 if (!
ExecQual(predicate, econtext))
1646 * For the current heap tuple, extract all the attributes we use in
1647 * this index, and note which are null. This also performs evaluation
1648 * of any expressions needed.
1657 * You'd think we should go ahead and build the index tuple here, but
1658 * some index AMs want to do further processing on the data first. So
1659 * pass the values[] and isnull[] arrays, instead.
1665 * For a heap-only tuple, pretend its TID is that of the root. See
1666 * src/backend/access/heap/README.HOT for discussion.
1674 * If a HOT tuple points to a root that we don't know about,
1675 * obtain root items afresh. If that still fails, report it as
1690 errmsg_internal(
"failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1696 root_offsets[offnum - 1]);
1698 /* Call the AM's callback routine to process the tuple */
1704 /* Call the AM's callback routine to process the tuple */
1706 tupleIsAlive, callback_state);
1710 /* Report scan progress one last time. */
1731 /* we can now forget our snapshot, if set and registered by us */
1732 if (need_unregister_snapshot)
1739 /* These may have been pointing to the now-gone estate */
1767 /* state variables for the merge */
1770 bool tuplesort_empty =
false;
1778 * Need an EState for evaluation of index expressions and partial-index
1779 * predicates. Also a slot to hold the current tuple.
1786 /* Arrange for econtext's scan tuple to be the tuple under test */
1789 /* Set up execution state for predicate, if any. */
1793 * Prepare for scan of the base relation. We need just those tuples
1794 * satisfying the passed-in reference snapshot. We must disable syncscan
1795 * here, because it's critical that we read from block zero forward to
1796 * match the sorted TIDs.
1799 snapshot,
/* snapshot */
1800 0,
/* number of keys */
1801 NULL,
/* scan key */
1802 true,
/* buffer access strategy OK */
1803 false);
/* syncscan not OK */
1810 * Scan all tuples matching the snapshot.
1831 * As commented in table_index_build_scan, we should index heap-only
1832 * tuples under the TIDs of their root tuples; so when we advance onto
1833 * a new heap page, build a map of root item offsets on the page.
1835 * This complicates merging against the tuplesort output: we will
1836 * visit the live tuples in order by their offsets, but the root
1837 * offsets that we need to compare against the index contents might be
1838 * ordered differently. So we might have to "look back" within the
1839 * tuplesort output, but only within the current page. We handle that
1840 * by keeping a bool array in_index[] showing all the
1841 * already-passed-over tuplesort output TIDs of the current page. We
1842 * clear that array here, when advancing onto a new heap page.
1852 memset(in_index, 0,
sizeof(in_index));
1857 /* Convert actual tuple TID to root TID */
1858 rootTuple = *heapcursor;
1863 root_offnum = root_offsets[root_offnum - 1];
1867 errmsg_internal(
"failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1875 * "merge" by skipping through the index tuples until we find or pass
1876 * the current root tuple.
1878 while (!tuplesort_empty &&
1888 * Remember index items seen earlier on the current heap page
1895 false, &ts_val, &ts_isnull,
1897 Assert(tuplesort_empty || !ts_isnull);
1898 if (!tuplesort_empty)
1901 indexcursor = &decoded;
1911 * If the tuplesort has overshot *and* we didn't see a match earlier,
1912 * then this tuple is missing from the index, so insert it.
1914 if ((tuplesort_empty ||
1916 !in_index[root_offnum - 1])
1920 /* Set up for predicate or expression evaluation */
1924 * In a partial index, discard tuples that don't satisfy the
1927 if (predicate != NULL)
1929 if (!
ExecQual(predicate, econtext))
1934 * For the current heap tuple, extract all the attributes we use
1935 * in this index, and note which are null. This also performs
1936 * evaluation of any expressions needed.
1945 * You'd think we should go ahead and build the index tuple here,
1946 * but some index AMs want to do further processing on the data
1947 * first. So pass the values[] and isnull[] arrays, instead.
1951 * If the tuple is already committed dead, you might think we
1952 * could suppress uniqueness checking, but this is no longer true
1953 * in the presence of HOT, because the insert is actually a proxy
1954 * for a uniqueness check on the whole HOT-chain. That is, the
1955 * tuple we have here could be dead because it was already
1956 * HOT-updated, and if so the updating transaction will not have
1957 * thought it should insert index entries. The index AM will
1958 * check the whole HOT-chain and correctly detect a conflict if
1972 state->tups_inserted += 1;
1982 /* These may have been pointing to the now-gone estate */
1988 * Return the number of blocks that have been read by this scan since
1989 * starting. This is meant for progress reporting rather than be fully
1990 * accurate: in a parallel scan, workers can be concurrently reading blocks
1991 * further ahead than what we report.
2009 * Might have wrapped around the end of the relation, if startblock was
2013 blocks_done = hscan->
rs_cblock - startblock;
2019 blocks_done = nblocks - startblock +
2027/* ------------------------------------------------------------------------
2028 * Miscellaneous callbacks for the heap AM
2029 * ------------------------------------------------------------------------
2033 * Check to see whether the table needs a TOAST table. It does only if
2034 * (1) there are any toastable attributes, and (2) the maximum length
2035 * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
2036 * create a toast table for something like "f1 varchar(20)".)
2041 int32 data_length = 0;
2042 bool maxlength_unknown =
false;
2043 bool has_toastable_attrs =
false;
2048 for (
i = 0;
i < tupdesc->
natts;
i++)
2052 if (att->attisdropped)
2054 if (att->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
2057 if (att->attlen > 0)
2059 /* Fixed-length types are never toastable */
2060 data_length += att->attlen;
2068 maxlength_unknown =
true;
2070 data_length += maxlen;
2071 if (att->attstorage != TYPSTORAGE_PLAIN)
2072 has_toastable_attrs =
true;
2075 if (!has_toastable_attrs)
2076 return false;
/* nothing to toast? */
2077 if (maxlength_unknown)
2078 return true;
/* any unlimited-length attrs? */
2086 * TOAST tables for heap relations are just heap relations.
2091 return rel->
rd_rel->relam;
2095/* ------------------------------------------------------------------------
2096 * Planner related callbacks for the heap AM
2097 * ------------------------------------------------------------------------
2100 #define HEAP_OVERHEAD_BYTES_PER_TUPLE \
2101 (MAXALIGN(SizeofHeapTupleHeader) + sizeof(ItemIdData))
2102 #define HEAP_USABLE_BYTES_PER_PAGE \
2103 (BLCKSZ - SizeOfPageHeaderData)
2117/* ------------------------------------------------------------------------
2118 * Executor related callbacks for the heap AM
2119 * ------------------------------------------------------------------------
2136 * Out of range? If so, nothing more to look at on this page
2141 * Returns false if the bitmap is exhausted and there are no further
2142 * blocks we need to scan.
2161 * Set up the result slot to point to this tuple. Note that the slot
2162 * acquires a pin on the buffer.
2180 /* return false immediately if relation is empty */
2184 /* release previous scan buffer, if any */
2195 /* scanning table sequentially */
2210 /* wrap to beginning of rel, might not have started at 0 */
2215 * Report our new scan position for synchronization purposes.
2217 * Note: we do this before checking for end of scan so that the
2218 * final state of the position hint is back at the start of the
2219 * rel. That's not strictly necessary, but otherwise when you run
2220 * the same query multiple times the starting position would shift
2221 * a little bit backwards on every invocation, which is confusing.
2222 * We don't guarantee any specific ordering in general, though.
2245 * Be sure to check for interrupts at least once per page. Checks at
2246 * higher code levels won't be able to stop a sample scan that encounters
2247 * many pages' worth of consecutive dead tuples.
2251 /* Read page using selected strategy */
2255 /* in pagemode, prune the page and determine visible tuple offsets */
2277 * When not using pagemode, we must lock the buffer during tuple
2278 * visibility checks.
2294 /* Ask the tablesample method which tuples to check on this page. */
2305 /* Skip invalid tuple pointers. */
2321 /* in pagemode, heap_prepare_pagescan did this for us */
2326 /* Try next tuple from same page. */
2330 /* Found visible tuple, return it. */
2336 /* Count successfully-fetched tuples as heap fetches */
2344 * If we get here, it means we've exhausted the items on this page
2345 * and it's time to move to the next.
2359/* ----------------------------------------------------------------------------
2360 * Helper functions for the above.
2361 * ----------------------------------------------------------------------------
2365 * Reconstruct and rewrite the given tuple
2367 * We cannot simply copy the tuple as-is, for several reasons:
2369 * 1. We'd like to squeeze out the values of any dropped columns, both
2370 * to save space and to ensure we have no corner-case failures. (It's
2371 * possible for example that the new table hasn't got a TOAST table
2372 * and so is unable to store any large values of dropped cols.)
2374 * 2. The tuple might not even be legal for the new table; this is
2375 * currently only known to happen as an after-effect of ALTER TABLE
2378 * So, we must reconstruct the tuple from component Datums.
2392 /* Be sure to null out any dropped columns */
2393 for (
i = 0;
i < newTupDesc->
natts;
i++)
2401 /* The heap rewrite module does the rest */
2408 * Check visibility of the tuple.
2423 * In pageatatime mode, heap_prepare_pagescan() already did visibility
2424 * checks, so just look at the info it left in rs_vistuples[].
2426 * We use a binary search over the known-sorted array. Note: we could
2427 * save some effort if we insisted that NextSampleTuple select tuples
2428 * in increasing order, but it's not clear that there would be enough
2429 * gain to justify the restriction.
2436 if (tupoffset == curoffset)
2438 else if (tupoffset < curoffset)
2448 /* Otherwise, we have to check the tuple individually. */
2455 * Helper function get the next block of a bitmap heap scan. Returns true when
2456 * it got the next block and saved it in the scan descriptor and false when
2457 * the bitmap and or relation are exhausted.
2467 void *per_buffer_data;
2481 /* Release buffer containing previous block. */
2493 /* the bitmap is exhausted */
2499 tbmres = per_buffer_data;
2504 /* Exact pages need their tuple offsets extracted. */
2518 * Prune and repair fragmentation for the whole page, if possible.
2523 * We must hold share lock on the buffer content while examining tuple
2524 * visibility. Afterwards, however, the tuples we have found to be
2525 * visible are guaranteed good as long as we hold the buffer pin.
2530 * We need two separate strategies for lossy and non-lossy cases.
2535 * Bitmap is non-lossy, so we just look through the offsets listed in
2536 * tbmres; but we have to follow any HOT chain starting at each such
2541 /* We must have extracted the tuple offsets by now */
2544 for (curslot = 0; curslot < noffsets; curslot++)
2552 &heapTuple, NULL,
true))
2559 * Bitmap is lossy, so we must examine each line pointer on the page.
2560 * But we can ignore HOT chains, since we'll check each tuple anyway.
2602 * Return true to indicate that a valid block was found and the bitmap is
2603 * not exhausted. If there are no visible tuples on this page,
2604 * hscan->rs_ntuples will be 0 and heapam_scan_bitmap_next_tuple() will
2605 * return false returning control to this function to advance to the next
2606 * block in the bitmap.
2611/* ------------------------------------------------------------------------
2612 * Definition of the heap table access method.
2613 * ------------------------------------------------------------------------
2617 .
type = T_TableAmRoutine,
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_update_multi_param(int nparam, const int *index, const int64 *val)
#define InvalidBlockNumber
static bool BlockNumberIsValid(BlockNumber blockNumber)
static Datum values[MAXATTR]
#define BufferIsInvalid(buffer)
BlockNumber BufferGetBlockNumber(Buffer buffer)
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
void ReleaseBuffer(Buffer buffer)
void UnlockReleaseBuffer(Buffer buffer)
void LockBuffer(Buffer buffer, int mode)
void FlushRelationBuffers(Relation rel)
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
#define BUFFER_LOCK_UNLOCK
#define BUFFER_LOCK_SHARE
static Page BufferGetPage(Buffer buffer)
static bool BufferIsValid(Buffer bufnum)
static bool PageIsAllVisible(const PageData *page)
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
TransactionId MultiXactId
#define OidIsValid(objectId)
bool IsSystemRelation(Relation relation)
CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
int errmsg_internal(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
ExprState * ExecPrepareQual(List *qual, EState *estate)
TupleTableSlot * ExecStorePinnedBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
const TupleTableSlotOps TTSOpsBufferHeapTuple
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
const TupleTableSlotOps TTSOpsHeapTuple
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
void FreeExecutorState(EState *estate)
EState * CreateExecutorState(void)
#define GetPerTupleExprContext(estate)
static bool ExecQual(ExprState *state, ExprContext *econtext)
#define PG_RETURN_POINTER(x)
Assert(PointerIsAligned(start, uint64))
void heap_finish_speculative(Relation relation, ItemPointer tid)
void heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf)
TM_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart)
bool heap_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
void heap_endscan(TableScanDesc sscan)
void heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode)
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)
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
bool heap_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
void heap_set_tidrange(TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
void heap_abort_speculative(Relation relation, ItemPointer tid)
TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key, ParallelTableScanDesc parallel_scan, uint32 flags)
void heap_prepare_pagescan(TableScanDesc sscan)
TransactionId heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
void heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, CommandId cid, int options, BulkInsertState bistate)
TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, Buffer *buffer, TM_FailureData *tmfd)
void heap_get_latest_tid(TableScanDesc sscan, ItemPointer tid)
void heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
#define HEAP_INSERT_SPECULATIVE
struct HeapScanDescData * HeapScanDesc
@ HEAPTUPLE_RECENTLY_DEAD
@ HEAPTUPLE_INSERT_IN_PROGRESS
@ HEAPTUPLE_DELETE_IN_PROGRESS
struct BitmapHeapScanDescData * BitmapHeapScanDesc
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)
#define HEAP_OVERHEAD_BYTES_PER_TUPLE
static void heapam_estimate_rel_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac)
static const TableAmRoutine heapam_methods
static bool BitmapHeapScanNextBlock(TableScanDesc scan, bool *recheck, uint64 *lossy_pages, uint64 *exact_pages)
static void heapam_index_validate_scan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, Snapshot snapshot, ValidateIndexState *state)
static bool heapam_index_fetch_tuple(struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *call_again, bool *all_dead)
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 void heapam_tuple_insert_speculative(Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate, uint32 specToken)
static bool heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate)
static void heapam_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate)
static bool heapam_scan_analyze_next_block(TableScanDesc scan, ReadStream *stream)
static TM_Result heapam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart)
static void heapam_index_fetch_reset(IndexFetchTableData *scan)
static Oid heapam_relation_toast_am(Relation rel)
const TableAmRoutine * GetHeapamTableAmRoutine(void)
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)
static bool heapam_relation_needs_toast_table(Relation rel)
static BlockNumber heapam_scan_get_blocks_done(HeapScanDesc hscan)
static bool heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin, double *liverows, double *deadrows, TupleTableSlot *slot)
static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer, HeapTuple tuple, OffsetNumber tupoffset)
static bool heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate, TupleTableSlot *slot)
static IndexFetchTableData * heapam_index_fetch_begin(Relation rel)
static bool heapam_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
#define HEAP_USABLE_BYTES_PER_PAGE
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_copy_data(Relation rel, const RelFileLocator *newrlocator)
static void heapam_index_fetch_end(IndexFetchTableData *scan)
static void heapam_tuple_complete_speculative(Relation relation, TupleTableSlot *slot, uint32 specToken, bool succeeded)
static void reform_and_rewrite_tuple(HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull, RewriteState rwstate)
static void heapam_relation_set_new_filelocator(Relation rel, const RelFileLocator *newrlocator, char persistence, TransactionId *freezeXid, MultiXactId *minmulti)
static bool heapam_fetch_row_version(Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
static bool heapam_scan_bitmap_next_tuple(TableScanDesc scan, TupleTableSlot *slot, bool *recheck, uint64 *lossy_pages, uint64 *exact_pages)
static void heapam_relation_nontransactional_truncate(Relation rel)
static bool heapam_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Datum heap_tableam_handler(PG_FUNCTION_ARGS)
static const TupleTableSlotOps * heapam_slot_callbacks(Relation relation)
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
void heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, struct varlena *result)
#define TOAST_TUPLE_THRESHOLD
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
void heap_freetuple(HeapTuple htup)
HeapTupleHeaderData * HeapTupleHeader
static bool HeapTupleIsHotUpdated(const HeapTupleData *tuple)
#define SizeofHeapTupleHeader
static void HeapTupleHeaderSetSpeculativeToken(HeapTupleHeaderData *tup, BlockNumber token)
static int BITMAPLEN(int NATTS)
static bool HeapTupleIsHeapOnly(const HeapTupleData *tuple)
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
static bool HeapTupleHeaderIsSpeculative(const HeapTupleHeaderData *tup)
static TransactionId HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
#define MaxHeapTuplesPerPage
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
static void itemptr_decode(ItemPointer itemptr, int64 encoded)
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
bool index_insert(Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_t_ctid, Relation heapRelation, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, IndexScanInstrumentation *instrument, int nkeys, int norderbys)
void index_endscan(IndexScanDesc scan)
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
#define ItemIdGetLength(itemId)
#define ItemIdIsNormal(itemId)
#define ItemIdIsDead(itemId)
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
static bool ItemPointerIsValid(const ItemPointerData *pointer)
bool ConditionalXactLockTableWait(TransactionId xid, bool logLockFailure)
void XactLockTableWait(TransactionId xid, Relation rel, const ItemPointerData *ctid, XLTW_Oper oper)
void MemoryContextReset(MemoryContext context)
void pfree(void *pointer)
void * palloc0(Size size)
#define IsBootstrapProcessingMode()
#define CHECK_FOR_INTERRUPTS()
MultiXactId GetOldestMultiXactId(void)
#define InvalidOffsetNumber
#define OffsetNumberIsValid(offsetNumber)
#define OffsetNumberNext(offsetNumber)
#define FirstOffsetNumber
FormData_pg_attribute * Form_pg_attribute
#define ERRCODE_DATA_CORRUPTED
static PgChecksumMode mode
#define ERRCODE_T_R_SERIALIZATION_FAILURE
#define pgstat_count_heap_fetch(rel)
#define pgstat_count_heap_getnext(rel)
static int64 DatumGetInt64(Datum X)
void PredicateLockTID(Relation relation, ItemPointer tid, Snapshot snapshot, TransactionId tuple_xid)
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
#define PROGRESS_CLUSTER_INDEX_RELID
#define PROGRESS_CLUSTER_HEAP_BLKS_SCANNED
#define PROGRESS_CLUSTER_PHASE_SORT_TUPLES
#define PROGRESS_CLUSTER_PHASE_SEQ_SCAN_HEAP
#define PROGRESS_SCAN_BLOCKS_DONE
#define PROGRESS_CLUSTER_PHASE
#define PROGRESS_CLUSTER_HEAP_TUPLES_SCANNED
#define PROGRESS_CLUSTER_TOTAL_HEAP_BLKS
#define PROGRESS_CLUSTER_HEAP_TUPLES_WRITTEN
#define PROGRESS_CLUSTER_PHASE_INDEX_SCAN_HEAP
#define PROGRESS_SCAN_BLOCKS_TOTAL
#define PROGRESS_CLUSTER_PHASE_WRITE_NEW_HEAP
void heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
void heap_page_prune_opt(Relation relation, Buffer buffer)
Buffer read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
#define RelationGetRelid(relation)
static SMgrRelation RelationGetSmgr(Relation rel)
#define RelationGetDescr(relation)
#define RelationGetRelationName(relation)
#define RelationGetTargetBlock(relation)
#define RelationIsPermanent(relation)
struct ParallelBlockTableScanDescData * ParallelBlockTableScanDesc
void end_heap_rewrite(RewriteState state)
bool rewrite_heap_dead_tuple(RewriteState state, HeapTuple old_tuple)
RewriteState begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xmin, TransactionId freeze_xid, MultiXactId cutoff_multi)
void rewrite_heap_tuple(RewriteState state, HeapTuple old_tuple, HeapTuple new_tuple)
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
void smgrclose(SMgrRelation reln)
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Snapshot GetTransactionSnapshot(void)
void UnregisterSnapshot(Snapshot snapshot)
Snapshot RegisterSnapshot(Snapshot snapshot)
#define InitDirtySnapshot(snapshotdata)
#define IsMVCCSnapshot(snapshot)
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
void RelationDropStorage(Relation rel)
void RelationTruncate(Relation rel, BlockNumber nblocks)
MemoryContext ecxt_per_tuple_memory
TupleTableSlot * ecxt_scantuple
BufferAccessStrategy rs_strategy
BlockNumber rs_startblock
OffsetNumber rs_vistuples[MaxHeapTuplesPerPage]
ReadStream * rs_read_stream
TableScanDescData rs_base
IndexFetchTableData xs_base
ExprState * ii_PredicateState
List * ii_ExpressionsState
BlockNumber phs_startblock
struct TsmRoutine * tsmroutine
struct SnapshotData * rs_snapshot
struct ParallelTableScanDescData * rs_parallel
NextSampleTuple_function NextSampleTuple
NextSampleBlock_function NextSampleBlock
void ss_report_location(Relation rel, BlockNumber location)
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Size table_block_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan)
void table_block_parallelscan_reinitialize(Relation rel, ParallelTableScanDesc pscan)
uint64 table_block_relation_size(Relation rel, ForkNumber forkNumber)
Size table_block_parallelscan_estimate(Relation rel)
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)
static void table_endscan(TableScanDesc scan)
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
void(* IndexBuildCallback)(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
static TableScanDesc table_beginscan_strat(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key, bool allow_strat, bool allow_sync)
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key)
#define TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
int tbm_extract_page_tuple(TBMIterateResult *iteritem, OffsetNumber *offsets, uint32 max_offsets)
#define TBM_MAX_TUPLES_PER_PAGE
#define InvalidTransactionId
#define TransactionIdEquals(id1, id2)
#define TransactionIdIsValid(xid)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
void tuplesort_performsort(Tuplesortstate *state)
void tuplesort_end(Tuplesortstate *state)
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)
bool tuplesort_getdatum(Tuplesortstate *state, bool forward, bool copy, Datum *val, bool *isNull, Datum *abbrev)
#define att_align_nominal(cur_offset, attalign)
#define TTS_IS_BUFFERTUPLE(slot)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
void heap_vacuum_rel(Relation rel, const VacuumParams params, BufferAccessStrategy bstrategy)
bool TransactionIdIsCurrentTransactionId(TransactionId xid)