PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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/visibilitymap.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/lock.h"
#include "storage/predicate.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/tuplesort.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 void reform_and_rewrite_tuple (HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull, RewriteState rwstate)
 
static void heap_insert_for_repack (HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull, BulkInsertState bistate)
 
static HeapTuple reform_tuple (HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull)
 
static bool SampleHeapTupleVisible (TableScanDesc scan, Buffer buffer, HeapTuple tuple, OffsetNumber tupoffset)
 
static BlockNumber heapam_scan_get_blocks_done (HeapScanDesc hscan)
 
static bool BitmapHeapScanNextBlock (TableScanDesc scan, bool *recheck, uint64 *lossy_pages, uint64 *exact_pages)
 
static const TupleTableSlotOpsheapam_slot_callbacks (Relation relation)
 
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, uint32 options, BulkInsertState bistate)
 
static void heapam_tuple_insert_speculative (Relation relation, TupleTableSlot *slot, CommandId cid, uint32 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, uint32 options, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd)
 
static TM_Result heapam_tuple_update (Relation relation, ItemPointer otid, TupleTableSlot *slot, CommandId cid, uint32 options, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
 
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, Snapshot snapshot, 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, ReadStream *stream)
 
static bool heapam_scan_analyze_next_tuple (TableScanDesc scan, 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_tuple (TableScanDesc scan, TupleTableSlot *slot, bool *recheck, uint64 *lossy_pages, uint64 *exact_pages)
 
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 2065 of file heapam_handler.c.

2074{
2076 tuples, allvisfrac,
2079}
2080
2081
2082/* ------------------------------------------------------------------------
2083 * Executor related callbacks for the heap AM
2084 * ------------------------------------------------------------------------
2085 */
2086
2087static bool
2089 TupleTableSlot *slot,
2090 bool *recheck,
2091 uint64 *lossy_pages,
2092 uint64 *exact_pages)
2093{
2097 Page page;
2098 ItemId lp;
2099
2100 /*
2101 * Out of range? If so, nothing more to look at on this page
2102 */
2103 while (hscan->rs_cindex >= hscan->rs_ntuples)
2104 {
2105 /*
2106 * Returns false if the bitmap is exhausted and there are no further
2107 * blocks we need to scan.
2108 */
2109 if (!BitmapHeapScanNextBlock(scan, recheck, lossy_pages, exact_pages))
2110 return false;
2111 }
2112
2113 targoffset = hscan->rs_vistuples[hscan->rs_cindex];
2114 page = BufferGetPage(hscan->rs_cbuf);
2115 lp = PageGetItemId(page, targoffset);
2117
2118 hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2119 hscan->rs_ctup.t_len = ItemIdGetLength(lp);
2120 hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
2121 ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);
2122
2124
2125 /*
2126 * Set up the result slot to point to this tuple. Note that the slot
2127 * acquires a pin on the buffer.
2128 */
2130 slot,
2131 hscan->rs_cbuf);
2132
2133 hscan->rs_cindex++;
2134
2135 return true;
2136}
2137
2138static bool
2140{
2142 TsmRoutine *tsm = scanstate->tsmroutine;
2143 BlockNumber blockno;
2144
2145 /* return false immediately if relation is empty */
2146 if (hscan->rs_nblocks == 0)
2147 return false;
2148
2149 /* release previous scan buffer, if any */
2150 if (BufferIsValid(hscan->rs_cbuf))
2151 {
2152 ReleaseBuffer(hscan->rs_cbuf);
2153 hscan->rs_cbuf = InvalidBuffer;
2154 }
2155
2156 if (tsm->NextSampleBlock)
2157 blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks);
2158 else
2159 {
2160 /* scanning table sequentially */
2161
2162 if (hscan->rs_cblock == InvalidBlockNumber)
2163 {
2164 Assert(!hscan->rs_inited);
2165 blockno = hscan->rs_startblock;
2166 }
2167 else
2168 {
2169 Assert(hscan->rs_inited);
2170
2171 blockno = hscan->rs_cblock + 1;
2172
2173 if (blockno >= hscan->rs_nblocks)
2174 {
2175 /* wrap to beginning of rel, might not have started at 0 */
2176 blockno = 0;
2177 }
2178
2179 /*
2180 * Report our new scan position for synchronization purposes.
2181 *
2182 * Note: we do this before checking for end of scan so that the
2183 * final state of the position hint is back at the start of the
2184 * rel. That's not strictly necessary, but otherwise when you run
2185 * the same query multiple times the starting position would shift
2186 * a little bit backwards on every invocation, which is confusing.
2187 * We don't guarantee any specific ordering in general, though.
2188 */
2189 if (scan->rs_flags & SO_ALLOW_SYNC)
2190 ss_report_location(scan->rs_rd, blockno);
2191
2192 if (blockno == hscan->rs_startblock)
2193 {
2194 blockno = InvalidBlockNumber;
2195 }
2196 }
2197 }
2198
2199 hscan->rs_cblock = blockno;
2200
2201 if (!BlockNumberIsValid(blockno))
2202 {
2203 hscan->rs_inited = false;
2204 return false;
2205 }
2206
2207 Assert(hscan->rs_cblock < hscan->rs_nblocks);
2208
2209 /*
2210 * Be sure to check for interrupts at least once per page. Checks at
2211 * higher code levels won't be able to stop a sample scan that encounters
2212 * many pages' worth of consecutive dead tuples.
2213 */
2215
2216 /* Read page using selected strategy */
2217 hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
2218 blockno, RBM_NORMAL, hscan->rs_strategy);
2219
2220 /* in pagemode, prune the page and determine visible tuple offsets */
2221 if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
2223
2224 hscan->rs_inited = true;
2225 return true;
2226}
2227
2228static bool
2230 TupleTableSlot *slot)
2231{
2233 TsmRoutine *tsm = scanstate->tsmroutine;
2234 BlockNumber blockno = hscan->rs_cblock;
2235 bool pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
2236
2237 Page page;
2238 bool all_visible;
2240
2241 /*
2242 * When not using pagemode, we must lock the buffer during tuple
2243 * visibility checks.
2244 */
2245 if (!pagemode)
2247
2248 page = BufferGetPage(hscan->rs_cbuf);
2249 all_visible = PageIsAllVisible(page) &&
2252
2253 for (;;)
2254 {
2256
2258
2259 /* Ask the tablesample method which tuples to check on this page. */
2260 tupoffset = tsm->NextSampleTuple(scanstate,
2261 blockno,
2262 maxoffset);
2263
2265 {
2266 ItemId itemid;
2267 bool visible;
2268 HeapTuple tuple = &(hscan->rs_ctup);
2269
2270 /* Skip invalid tuple pointers. */
2271 itemid = PageGetItemId(page, tupoffset);
2272 if (!ItemIdIsNormal(itemid))
2273 continue;
2274
2275 tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
2276 tuple->t_len = ItemIdGetLength(itemid);
2277 ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
2278
2279
2280 if (all_visible)
2281 visible = true;
2282 else
2283 visible = SampleHeapTupleVisible(scan, hscan->rs_cbuf,
2284 tuple, tupoffset);
2285
2286 /* in pagemode, heap_prepare_pagescan did this for us */
2287 if (!pagemode)
2288 HeapCheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
2289 hscan->rs_cbuf, scan->rs_snapshot);
2290
2291 /* Try next tuple from same page. */
2292 if (!visible)
2293 continue;
2294
2295 /* Found visible tuple, return it. */
2296 if (!pagemode)
2298
2299 ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf);
2300
2301 /* Count successfully-fetched tuples as heap fetches */
2303
2304 return true;
2305 }
2306 else
2307 {
2308 /*
2309 * If we get here, it means we've exhausted the items on this page
2310 * and it's time to move to the next.
2311 */
2312 if (!pagemode)
2314
2315 ExecClearTuple(slot);
2316 return false;
2317 }
2318 }
2319
2320 Assert(0);
2321}
2322
2323
2324/* ----------------------------------------------------------------------------
2325 * Helper functions for the above.
2326 * ----------------------------------------------------------------------------
2327 */
2328
2329/*
2330 * Reconstruct and rewrite the given tuple
2331 *
2332 * We cannot simply copy the tuple as-is, for several reasons:
2333 *
2334 * 1. We'd like to squeeze out the values of any dropped columns, both
2335 * to save space and to ensure we have no corner-case failures. (It's
2336 * possible for example that the new table hasn't got a TOAST table
2337 * and so is unable to store any large values of dropped cols.)
2338 *
2339 * 2. The tuple might not even be legal for the new table; this is
2340 * currently only known to happen as an after-effect of ALTER TABLE
2341 * SET WITHOUT OIDS.
2342 *
2343 * So, we must reconstruct the tuple from component Datums.
2344 */
2345static void
2348 Datum *values, bool *isnull, RewriteState rwstate)
2349{
2350 HeapTuple newtuple;
2351
2352 newtuple = reform_tuple(tuple, OldHeap, NewHeap, values, isnull);
2353
2354 /* The heap rewrite module does the rest */
2355 rewrite_heap_tuple(rwstate, tuple, newtuple);
2356
2357 heap_freetuple(newtuple);
2358}
2359
2360/*
2361 * Insert tuple when processing REPACK CONCURRENTLY.
2362 *
2363 * rewriteheap.c is not used in the CONCURRENTLY case because it'd be
2364 * difficult to do the same in the catch-up phase (as the logical
2365 * decoding does not provide us with sufficient visibility
2366 * information). Thus we must use heap_insert() both during the
2367 * catch-up and here.
2368 *
2369 * We pass the NO_LOGICAL flag to heap_insert() in order to skip logical
2370 * decoding: as soon as REPACK CONCURRENTLY swaps the relation files, it drops
2371 * this relation, so no logical replication subscription should need the data.
2372 *
2373 * BulkInsertState is used because many tuples are inserted in the typical
2374 * case.
2375 */
2376static void
2378 Datum *values, bool *isnull, BulkInsertState bistate)
2379{
2380 HeapTuple newtuple;
2381
2382 newtuple = reform_tuple(tuple, OldHeap, NewHeap, values, isnull);
2383
2384 heap_insert(NewHeap, newtuple, GetCurrentCommandId(true),
2385 HEAP_INSERT_NO_LOGICAL, bistate);
2386
2387 heap_freetuple(newtuple);
2388}
2389
2390/*
2391 * Subroutine for reform_and_rewrite_tuple and heap_insert_for_repack.
2392 *
2393 * Deform the given tuple, set values of dropped columns to NULL, and fill in
2394 * any values from attmissingval; then form a new tuple and return it. If no
2395 * attributes need to be changed, a copy of the original tuple is returned.
2396 * Caller is responsible for freeing the returned tuple.
2397 *
2398 * XXX this coding assumes that both relations have the same tupledesc.
2399 */
2400static HeapTuple
2402 Datum *values, bool *isnull)
2403{
2406 bool needs_reform = false;
2407
2408 /*
2409 * A short tuple might require values from attmissing val, so activate the
2410 * coding unconditionally in that case. The value might legitimally be
2411 * NULL otherwise, so this is slightly wasteful, but it probably beats
2412 * having to test each attribute for presence of attmissingval each time.
2413 */
2414 if (HeapTupleHeaderGetNatts(tuple->t_data) < newTupDesc->natts)
2415 needs_reform = true;
2416
2417 /*
2418 * If the column has been dropped but a value is still present, we can
2419 * optimize storage now by getting rid of it.
2420 */
2421 if (!needs_reform)
2422 {
2423 for (int i = 0; i < newTupDesc->natts; i++)
2424 {
2425 if (TupleDescCompactAttr(newTupDesc, i)->attisdropped &&
2426 !heap_attisnull(tuple, i + 1, newTupDesc))
2427 {
2428 needs_reform = true;
2429 break;
2430 }
2431 }
2432 }
2433
2434 /* Skip work if no changes are needed */
2435 if (!needs_reform)
2436 return heap_copytuple(tuple);
2437
2438 heap_deform_tuple(tuple, oldTupDesc, values, isnull);
2439
2440 for (int i = 0; i < newTupDesc->natts; i++)
2441 {
2442 if (TupleDescCompactAttr(newTupDesc, i)->attisdropped)
2443 isnull[i] = true;
2444 }
2445
2446 return heap_form_tuple(newTupDesc, values, isnull);
2447}
2448
2449/*
2450 * Check visibility of the tuple.
2451 */
2452static bool
2454 HeapTuple tuple,
2456{
2458
2459 if (scan->rs_flags & SO_ALLOW_PAGEMODE)
2460 {
2461 uint32 start = 0,
2462 end = hscan->rs_ntuples;
2463
2464 /*
2465 * In pageatatime mode, heap_prepare_pagescan() already did visibility
2466 * checks, so just look at the info it left in rs_vistuples[].
2467 *
2468 * We use a binary search over the known-sorted array. Note: we could
2469 * save some effort if we insisted that NextSampleTuple select tuples
2470 * in increasing order, but it's not clear that there would be enough
2471 * gain to justify the restriction.
2472 */
2473 while (start < end)
2474 {
2475 uint32 mid = start + (end - start) / 2;
2476 OffsetNumber curoffset = hscan->rs_vistuples[mid];
2477
2478 if (tupoffset == curoffset)
2479 return true;
2480 else if (tupoffset < curoffset)
2481 end = mid;
2482 else
2483 start = mid + 1;
2484 }
2485
2486 return false;
2487 }
2488 else
2489 {
2490 /* Otherwise, we have to check the tuple individually. */
2491 return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
2492 buffer);
2493 }
2494}
2495
2496/*
2497 * Helper function get the next block of a bitmap heap scan. Returns true when
2498 * it got the next block and saved it in the scan descriptor and false when
2499 * the bitmap and or relation are exhausted.
2500 */
2501static bool
2503 bool *recheck,
2504 uint64 *lossy_pages, uint64 *exact_pages)
2505{
2508 BlockNumber block;
2509 void *per_buffer_data;
2510 Buffer buffer;
2511 Snapshot snapshot;
2512 int ntup;
2515 int noffsets = -1;
2516
2518 Assert(hscan->rs_read_stream);
2519
2520 hscan->rs_cindex = 0;
2521 hscan->rs_ntuples = 0;
2522
2523 /* Release buffer containing previous block. */
2524 if (BufferIsValid(hscan->rs_cbuf))
2525 {
2526 ReleaseBuffer(hscan->rs_cbuf);
2527 hscan->rs_cbuf = InvalidBuffer;
2528 }
2529
2530 hscan->rs_cbuf = read_stream_next_buffer(hscan->rs_read_stream,
2531 &per_buffer_data);
2532
2533 if (BufferIsInvalid(hscan->rs_cbuf))
2534 {
2535 /* the bitmap is exhausted */
2536 return false;
2537 }
2538
2539 Assert(per_buffer_data);
2540
2541 tbmres = per_buffer_data;
2542
2543 Assert(BlockNumberIsValid(tbmres->blockno));
2544 Assert(BufferGetBlockNumber(hscan->rs_cbuf) == tbmres->blockno);
2545
2546 /* Exact pages need their tuple offsets extracted. */
2547 if (!tbmres->lossy)
2550
2551 *recheck = tbmres->recheck;
2552
2553 block = hscan->rs_cblock = tbmres->blockno;
2554 buffer = hscan->rs_cbuf;
2555 snapshot = scan->rs_snapshot;
2556
2557 ntup = 0;
2558
2559 /*
2560 * Prune and repair fragmentation for the whole page, if possible.
2561 */
2562 heap_page_prune_opt(scan->rs_rd, buffer, &hscan->rs_vmbuffer,
2564
2565 /*
2566 * We must hold share lock on the buffer content while examining tuple
2567 * visibility. Afterwards, however, the tuples we have found to be
2568 * visible are guaranteed good as long as we hold the buffer pin.
2569 */
2571
2572 /*
2573 * We need two separate strategies for lossy and non-lossy cases.
2574 */
2575 if (!tbmres->lossy)
2576 {
2577 /*
2578 * Bitmap is non-lossy, so we just look through the offsets listed in
2579 * tbmres; but we have to follow any HOT chain starting at each such
2580 * offset.
2581 */
2582 int curslot;
2583
2584 /* We must have extracted the tuple offsets by now */
2585 Assert(noffsets > -1);
2586
2587 for (curslot = 0; curslot < noffsets; curslot++)
2588 {
2589 OffsetNumber offnum = offsets[curslot];
2590 ItemPointerData tid;
2592
2593 ItemPointerSet(&tid, block, offnum);
2594 if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
2595 &heapTuple, NULL, true))
2596 hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
2597 }
2598 }
2599 else
2600 {
2601 /*
2602 * Bitmap is lossy, so we must examine each line pointer on the page.
2603 * But we can ignore HOT chains, since we'll check each tuple anyway.
2604 */
2605 Page page = BufferGetPage(buffer);
2606 OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
2607 OffsetNumber offnum;
2608
2609 for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
2610 {
2611 ItemId lp;
2613 bool valid;
2614
2615 lp = PageGetItemId(page, offnum);
2616 if (!ItemIdIsNormal(lp))
2617 continue;
2618 loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2619 loctup.t_len = ItemIdGetLength(lp);
2620 loctup.t_tableOid = scan->rs_rd->rd_id;
2621 ItemPointerSet(&loctup.t_self, block, offnum);
2622 valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
2623 if (valid)
2624 {
2625 hscan->rs_vistuples[ntup++] = offnum;
2626 PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
2628 }
2630 buffer, snapshot);
2631 }
2632 }
2633
2635
2637 hscan->rs_ntuples = ntup;
2638
2639 if (tbmres->lossy)
2640 (*lossy_pages)++;
2641 else
2642 (*exact_pages)++;
2643
2644 /*
2645 * Return true to indicate that a valid block was found and the bitmap is
2646 * not exhausted. If there are no visible tuples on this page,
2647 * hscan->rs_ntuples will be 0 and heapam_scan_bitmap_next_tuple() will
2648 * return false returning control to this function to advance to the next
2649 * block in the bitmap.
2650 */
2651 return true;
2652}
2653
2654/* ------------------------------------------------------------------------
2655 * Definition of the heap table access method.
2656 * ------------------------------------------------------------------------
2657 */
2658
2659static const TableAmRoutine heapam_methods = {
2661
2662 .slot_callbacks = heapam_slot_callbacks,
2663
2664 .scan_begin = heap_beginscan,
2665 .scan_end = heap_endscan,
2666 .scan_rescan = heap_rescan,
2667 .scan_getnextslot = heap_getnextslot,
2668
2669 .scan_set_tidrange = heap_set_tidrange,
2670 .scan_getnextslot_tidrange = heap_getnextslot_tidrange,
2671
2672 .parallelscan_estimate = table_block_parallelscan_estimate,
2673 .parallelscan_initialize = table_block_parallelscan_initialize,
2674 .parallelscan_reinitialize = table_block_parallelscan_reinitialize,
2675
2676 .index_fetch_begin = heapam_index_fetch_begin,
2677 .index_fetch_reset = heapam_index_fetch_reset,
2678 .index_fetch_end = heapam_index_fetch_end,
2679 .index_fetch_tuple = heapam_index_fetch_tuple,
2680
2681 .tuple_insert = heapam_tuple_insert,
2682 .tuple_insert_speculative = heapam_tuple_insert_speculative,
2683 .tuple_complete_speculative = heapam_tuple_complete_speculative,
2684 .multi_insert = heap_multi_insert,
2685 .tuple_delete = heapam_tuple_delete,
2686 .tuple_update = heapam_tuple_update,
2687 .tuple_lock = heapam_tuple_lock,
2688
2689 .tuple_fetch_row_version = heapam_fetch_row_version,
2690 .tuple_get_latest_tid = heap_get_latest_tid,
2691 .tuple_tid_valid = heapam_tuple_tid_valid,
2692 .tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,
2693 .index_delete_tuples = heap_index_delete_tuples,
2694
2695 .relation_set_new_filelocator = heapam_relation_set_new_filelocator,
2696 .relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,
2697 .relation_copy_data = heapam_relation_copy_data,
2698 .relation_copy_for_cluster = heapam_relation_copy_for_cluster,
2699 .relation_vacuum = heap_vacuum_rel,
2700 .scan_analyze_next_block = heapam_scan_analyze_next_block,
2701 .scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
2702 .index_build_range_scan = heapam_index_build_range_scan,
2703 .index_validate_scan = heapam_index_validate_scan,
2704
2705 .relation_size = table_block_relation_size,
2706 .relation_needs_toast_table = heapam_relation_needs_toast_table,
2707 .relation_toast_am = heapam_relation_toast_am,
2708 .relation_fetch_toast_slice = heap_fetch_toast_slice,
2709
2710 .relation_estimate_size = heapam_estimate_rel_size,
2711
2712 .scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,
2713 .scan_sample_next_block = heapam_scan_sample_next_block,
2714 .scan_sample_next_tuple = heapam_scan_sample_next_tuple
2715};
2716
2717
2718const TableAmRoutine *
2720{
2721 return &heapam_methods;
2722}
2723
2724Datum
2726{
2728}
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition block.h:71
static Datum values[MAXATTR]
Definition bootstrap.c:190
int Buffer
Definition buf.h:23
#define BufferIsInvalid(buffer)
Definition buf.h:31
#define InvalidBuffer
Definition buf.h:25
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4469
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5609
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition bufmgr.c:926
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:468
@ BUFFER_LOCK_SHARE
Definition bufmgr.h:212
@ BUFFER_LOCK_UNLOCK
Definition bufmgr.h:207
static void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition bufmgr.h:334
@ RBM_NORMAL
Definition bufmgr.h:46
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:419
static bool PageIsAllVisible(const PageData *page)
Definition bufpage.h:454
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition bufpage.h:268
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition bufpage.h:378
PageData * Page
Definition bufpage.h:81
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition bufpage.h:396
#define Assert(condition)
Definition c.h:1002
uint64_t uint64
Definition c.h:684
uint32_t uint32
Definition c.h:683
TupleTableSlot * ExecStoreBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
return str start
void heap_insert(Relation relation, HeapTuple tup, CommandId cid, uint32 options, BulkInsertState bistate)
Definition heapam.c:2004
bool heap_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition heapam.c:1474
void heap_endscan(TableScanDesc sscan)
Definition heapam.c:1390
void heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode)
Definition heapam.c:1331
bool heap_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition heapam.c:1577
void heap_set_tidrange(TableScanDesc sscan, ItemPointer mintid, ItemPointer maxtid)
Definition heapam.c:1504
void heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, CommandId cid, uint32 options, BulkInsertState bistate)
Definition heapam.c:2282
TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key, ParallelTableScanDesc parallel_scan, uint32 flags)
Definition heapam.c:1167
void heap_prepare_pagescan(TableScanDesc sscan)
Definition heapam.c:618
TransactionId heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
Definition heapam.c:8099
void heap_get_latest_tid(TableScanDesc sscan, ItemPointer tid)
Definition heapam.c:1793
void HeapCheckForSerializableConflictOut(bool visible, Relation relation, HeapTuple tuple, Buffer buffer, Snapshot snapshot)
Definition heapam.c:9183
struct HeapScanDescData * HeapScanDesc
Definition heapam.h:107
struct BitmapHeapScanDescData * BitmapHeapScanDesc
Definition heapam.h:115
#define HEAP_INSERT_NO_LOGICAL
Definition heapam.h:38
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 HeapTuple reform_tuple(HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull)
static void heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap, Relation OldIndex, bool use_sort, TransactionId OldestXmin, Snapshot snapshot, TransactionId *xid_cutoff, MultiXactId *multi_cutoff, double *num_tuples, double *tups_vacuumed, double *tups_recently_dead)
static bool heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate)
static void heapam_tuple_insert_speculative(Relation relation, TupleTableSlot *slot, CommandId cid, uint32 options, BulkInsertState bistate, uint32 specToken)
static bool heapam_scan_analyze_next_block(TableScanDesc scan, ReadStream *stream)
static void heap_insert_for_repack(HeapTuple tuple, Relation OldHeap, Relation NewHeap, Datum *values, bool *isnull, BulkInsertState bistate)
static TM_Result heapam_tuple_delete(Relation relation, ItemPointer tid, CommandId cid, uint32 options, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd)
static Oid heapam_relation_toast_am(Relation rel)
const TableAmRoutine * GetHeapamTableAmRoutine(void)
static bool heapam_relation_needs_toast_table(Relation rel)
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 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_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_analyze_next_tuple(TableScanDesc scan, double *liverows, double *deadrows, TupleTableSlot *slot)
static TM_Result heapam_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot, CommandId cid, uint32 options, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
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 void heapam_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid, uint32 options, BulkInsertState bistate)
static const TupleTableSlotOps * heapam_slot_callbacks(Relation relation)
void heapam_index_fetch_end(IndexFetchTableData *scan)
bool heapam_index_fetch_tuple(struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *heap_continue, bool *all_dead)
bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call)
IndexFetchTableData * heapam_index_fetch_begin(Relation rel, uint32 flags)
void heapam_index_fetch_reset(IndexFetchTableData *scan)
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
void heap_fetch_toast_slice(Relation toastrel, Oid valueid, int32 attrsize, int32 sliceoffset, int32 slicelength, varlena *result)
Definition heaptoast.c:626
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:686
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition heaptuple.c:1254
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
HeapTupleHeaderData * HeapTupleHeader
Definition htup.h:23
#define HeapTupleHeaderGetNatts(tup)
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
#define MaxHeapTuplesPerPage
int i
Definition isn.c:77
#define ItemIdGetLength(itemId)
Definition itemid.h:59
#define ItemIdIsNormal(itemId)
Definition itemid.h:99
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition itemptr.h:135
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition itemptr.h:124
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
#define OffsetNumberIsValid(offsetNumber)
Definition off.h:39
#define OffsetNumberNext(offsetNumber)
Definition off.h:52
uint16 OffsetNumber
Definition off.h:24
#define FirstOffsetNumber
Definition off.h:27
#define pgstat_count_heap_fetch(rel)
Definition pgstat.h:740
#define pgstat_count_heap_getnext(rel)
Definition pgstat.h:735
uint64_t Datum
Definition postgres.h:70
void PredicateLockTID(Relation relation, const ItemPointerData *tid, Snapshot snapshot, TransactionId tuple_xid)
Definition predicate.c:2550
static int fb(int x)
void heap_page_prune_opt(Relation relation, Buffer buffer, Buffer *vmbuffer, bool rel_read_only)
Definition pruneheap.c:272
Buffer read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
#define RelationGetDescr(relation)
Definition rel.h:542
@ MAIN_FORKNUM
Definition relpath.h:58
void rewrite_heap_tuple(RewriteState state, HeapTuple old_tuple, HeapTuple new_tuple)
uint32 rs_ntuples
Definition heapam.h:104
ItemPointerData t_self
Definition htup.h:65
uint32 t_len
Definition htup.h:64
HeapTupleHeader t_data
Definition htup.h:68
Oid rd_id
Definition rel.h:113
bool takenDuringRecovery
Definition snapshot.h:180
NodeTag type
Definition tableam.h:324
Relation rs_rd
Definition relscan.h:36
uint32 rs_flags
Definition relscan.h:64
struct SnapshotData * rs_snapshot
Definition relscan.h:37
void ss_report_location(Relation rel, BlockNumber location)
Definition syncscan.c:287
Size table_block_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan)
Definition tableam.c:414
void table_block_parallelscan_reinitialize(Relation rel, ParallelTableScanDesc pscan)
Definition tableam.c:433
uint64 table_block_relation_size(Relation rel, ForkNumber forkNumber)
Definition tableam.c:681
Size table_block_parallelscan_estimate(Relation rel)
Definition tableam.c:408
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:718
@ SO_HINT_REL_READ_ONLY
Definition tableam.h:71
@ SO_ALLOW_PAGEMODE
Definition tableam.h:65
@ SO_ALLOW_SYNC
Definition tableam.h:63
@ SO_TYPE_BITMAPSCAN
Definition tableam.h:53
int tbm_extract_page_tuple(TBMIterateResult *iteritem, OffsetNumber *offsets, uint32 max_offsets)
Definition tidbitmap.c:899
#define TBM_MAX_TUPLES_PER_PAGE
Definition tidbitmap.h:34
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
void heap_vacuum_rel(Relation rel, const VacuumParams *params, BufferAccessStrategy bstrategy)
Definition vacuumlazy.c:624
CommandId GetCurrentCommandId(bool used)
Definition xact.c:831

◆ HEAP_USABLE_BYTES_PER_PAGE

#define HEAP_USABLE_BYTES_PER_PAGE    (BLCKSZ - SizeOfPageHeaderData)

Definition at line 2067 of file heapam_handler.c.

Function Documentation

◆ BitmapHeapScanNextBlock()

static bool BitmapHeapScanNextBlock ( TableScanDesc  scan,
bool recheck,
uint64 lossy_pages,
uint64 exact_pages 
)
static

Definition at line 2502 of file heapam_handler.c.

2505{
2508 BlockNumber block;
2509 void *per_buffer_data;
2510 Buffer buffer;
2511 Snapshot snapshot;
2512 int ntup;
2515 int noffsets = -1;
2516
2518 Assert(hscan->rs_read_stream);
2519
2520 hscan->rs_cindex = 0;
2521 hscan->rs_ntuples = 0;
2522
2523 /* Release buffer containing previous block. */
2524 if (BufferIsValid(hscan->rs_cbuf))
2525 {
2526 ReleaseBuffer(hscan->rs_cbuf);
2527 hscan->rs_cbuf = InvalidBuffer;
2528 }
2529
2530 hscan->rs_cbuf = read_stream_next_buffer(hscan->rs_read_stream,
2531 &per_buffer_data);
2532
2533 if (BufferIsInvalid(hscan->rs_cbuf))
2534 {
2535 /* the bitmap is exhausted */
2536 return false;
2537 }
2538
2539 Assert(per_buffer_data);
2540
2541 tbmres = per_buffer_data;
2542
2543 Assert(BlockNumberIsValid(tbmres->blockno));
2544 Assert(BufferGetBlockNumber(hscan->rs_cbuf) == tbmres->blockno);
2545
2546 /* Exact pages need their tuple offsets extracted. */
2547 if (!tbmres->lossy)
2550
2551 *recheck = tbmres->recheck;
2552
2553 block = hscan->rs_cblock = tbmres->blockno;
2554 buffer = hscan->rs_cbuf;
2555 snapshot = scan->rs_snapshot;
2556
2557 ntup = 0;
2558
2559 /*
2560 * Prune and repair fragmentation for the whole page, if possible.
2561 */
2562 heap_page_prune_opt(scan->rs_rd, buffer, &hscan->rs_vmbuffer,
2564
2565 /*
2566 * We must hold share lock on the buffer content while examining tuple
2567 * visibility. Afterwards, however, the tuples we have found to be
2568 * visible are guaranteed good as long as we hold the buffer pin.
2569 */
2571
2572 /*
2573 * We need two separate strategies for lossy and non-lossy cases.
2574 */
2575 if (!tbmres->lossy)
2576 {
2577 /*
2578 * Bitmap is non-lossy, so we just look through the offsets listed in
2579 * tbmres; but we have to follow any HOT chain starting at each such
2580 * offset.
2581 */
2582 int curslot;
2583
2584 /* We must have extracted the tuple offsets by now */
2585 Assert(noffsets > -1);
2586
2587 for (curslot = 0; curslot < noffsets; curslot++)
2588 {
2589 OffsetNumber offnum = offsets[curslot];
2590 ItemPointerData tid;
2592
2593 ItemPointerSet(&tid, block, offnum);
2594 if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
2595 &heapTuple, NULL, true))
2596 hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
2597 }
2598 }
2599 else
2600 {
2601 /*
2602 * Bitmap is lossy, so we must examine each line pointer on the page.
2603 * But we can ignore HOT chains, since we'll check each tuple anyway.
2604 */
2605 Page page = BufferGetPage(buffer);
2606 OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
2607 OffsetNumber offnum;
2608
2609 for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum = OffsetNumberNext(offnum))
2610 {
2611 ItemId lp;
2613 bool valid;
2614
2615 lp = PageGetItemId(page, offnum);
2616 if (!ItemIdIsNormal(lp))
2617 continue;
2618 loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2619 loctup.t_len = ItemIdGetLength(lp);
2620 loctup.t_tableOid = scan->rs_rd->rd_id;
2621 ItemPointerSet(&loctup.t_self, block, offnum);
2622 valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
2623 if (valid)
2624 {
2625 hscan->rs_vistuples[ntup++] = offnum;
2626 PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
2628 }
2630 buffer, snapshot);
2631 }
2632 }
2633
2635
2637 hscan->rs_ntuples = ntup;
2638
2639 if (tbmres->lossy)
2640 (*lossy_pages)++;
2641 else
2642 (*exact_pages)++;
2643
2644 /*
2645 * Return true to indicate that a valid block was found and the bitmap is
2646 * not exhausted. If there are no visible tuples on this page,
2647 * hscan->rs_ntuples will be 0 and heapam_scan_bitmap_next_tuple() will
2648 * return false returning control to this function to advance to the next
2649 * block in the bitmap.
2650 */
2651 return true;
2652}

References Assert, BlockNumberIsValid(), BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), BufferIsInvalid, BufferIsValid(), fb(), FirstOffsetNumber, heap_hot_search_buffer(), heap_page_prune_opt(), HeapCheckForSerializableConflictOut(), HeapTupleHeaderGetXmin(), HeapTupleSatisfiesVisibility(), InvalidBuffer, ItemIdGetLength, ItemIdIsNormal, ItemPointerGetOffsetNumber(), ItemPointerSet(), LockBuffer(), MaxHeapTuplesPerPage, OffsetNumberNext, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PredicateLockTID(), RelationData::rd_id, read_stream_next_buffer(), ReleaseBuffer(), TableScanDescData::rs_flags, TableScanDescData::rs_rd, TableScanDescData::rs_snapshot, SO_HINT_REL_READ_ONLY, SO_TYPE_BITMAPSCAN, tbm_extract_page_tuple(), and TBM_MAX_TUPLES_PER_PAGE.

Referenced by heapam_scan_bitmap_next_tuple().

◆ GetHeapamTableAmRoutine()

const TableAmRoutine * GetHeapamTableAmRoutine ( void  )

Definition at line 2719 of file heapam_handler.c.

2720{
2721 return &heapam_methods;
2722}

References heapam_methods.

Referenced by formrdesc(), and heap_getnext().

◆ heap_insert_for_repack()

static void heap_insert_for_repack ( HeapTuple  tuple,
Relation  OldHeap,
Relation  NewHeap,
Datum values,
bool isnull,
BulkInsertState  bistate 
)
static

Definition at line 2377 of file heapam_handler.c.

2379{
2380 HeapTuple newtuple;
2381
2382 newtuple = reform_tuple(tuple, OldHeap, NewHeap, values, isnull);
2383
2384 heap_insert(NewHeap, newtuple, GetCurrentCommandId(true),
2385 HEAP_INSERT_NO_LOGICAL, bistate);
2386
2387 heap_freetuple(newtuple);
2388}

References fb(), GetCurrentCommandId(), heap_freetuple(), heap_insert(), HEAP_INSERT_NO_LOGICAL, reform_tuple(), and values.

Referenced by heapam_relation_copy_for_cluster().

◆ heap_tableam_handler()

Datum heap_tableam_handler ( PG_FUNCTION_ARGS  )

Definition at line 2725 of file heapam_handler.c.

2726{
2728}

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

◆ heapam_fetch_row_version()

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

Definition at line 89 of file heapam_handler.c.

93{
95 Buffer buffer;
96
98
99 bslot->base.tupdata.t_self = *tid;
100 if (heap_fetch(relation, snapshot, &bslot->base.tupdata, &buffer, false))
101 {
102 /* store in slot, transferring existing pin */
103 ExecStorePinnedBufferHeapTuple(&bslot->base.tupdata, slot, buffer);
104 slot->tts_tableOid = RelationGetRelid(relation);
105
106 return true;
107 }
108
109 return false;
110}
TupleTableSlot * ExecStorePinnedBufferHeapTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer)
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf)
Definition heapam.c:1684
#define RelationGetRelid(relation)
Definition rel.h:516
#define TTS_IS_BUFFERTUPLE(slot)
Definition tuptable.h:256

References Assert, ExecStorePinnedBufferHeapTuple(), fb(), 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 1137 of file heapam_handler.c.

1148{
1150 bool is_system_catalog;
1154 bool isnull[INDEX_MAX_KEYS];
1155 double reltuples;
1157 TupleTableSlot *slot;
1158 EState *estate;
1159 ExprContext *econtext;
1160 Snapshot snapshot;
1161 bool need_unregister_snapshot = false;
1162 TransactionId OldestXmin;
1164 BlockNumber root_blkno = InvalidBlockNumber;
1166
1167 /*
1168 * sanity checks
1169 */
1170 Assert(OidIsValid(indexRelation->rd_rel->relam));
1171
1172 /* Remember if it's a system catalog */
1173 is_system_catalog = IsSystemRelation(heapRelation);
1174
1175 /* See whether we're verifying uniqueness/exclusion properties */
1176 checking_uniqueness = (indexInfo->ii_Unique ||
1177 indexInfo->ii_ExclusionOps != NULL);
1178
1179 /*
1180 * "Any visible" mode is not compatible with uniqueness checks; make sure
1181 * only one of those is requested.
1182 */
1184
1185 /*
1186 * Need an EState for evaluation of index expressions and partial-index
1187 * predicates. Also a slot to hold the current tuple.
1188 */
1189 estate = CreateExecutorState();
1190 econtext = GetPerTupleExprContext(estate);
1191 slot = table_slot_create(heapRelation, NULL);
1192
1193 /* Arrange for econtext's scan tuple to be the tuple under test */
1194 econtext->ecxt_scantuple = slot;
1195
1196 /* Set up execution state for predicate, if any. */
1197 predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1198
1199 /*
1200 * Prepare for scan of the base relation. In a normal index build, we use
1201 * SnapshotAny because we must retrieve all tuples and do our own time
1202 * qual checks (because we have to index RECENTLY_DEAD tuples). In a
1203 * concurrent build, or during bootstrap, we take a regular MVCC snapshot
1204 * and index whatever's live according to that.
1205 */
1206 OldestXmin = InvalidTransactionId;
1207
1208 /* okay to ignore lazy VACUUMs here */
1209 if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)
1210 OldestXmin = GetOldestNonRemovableTransactionId(heapRelation);
1211
1212 if (!scan)
1213 {
1214 /*
1215 * Serial index build.
1216 *
1217 * Must begin our own heap scan in this case. We may also need to
1218 * register a snapshot whose lifetime is under our direct control.
1219 */
1220 if (!TransactionIdIsValid(OldestXmin))
1221 {
1224 }
1225 else
1226 snapshot = SnapshotAny;
1227
1228 scan = table_beginscan_strat(heapRelation, /* relation */
1229 snapshot, /* snapshot */
1230 0, /* number of keys */
1231 NULL, /* scan key */
1232 true, /* buffer access strategy OK */
1233 allow_sync); /* syncscan OK? */
1234 }
1235 else
1236 {
1237 /*
1238 * Parallel index build.
1239 *
1240 * Parallel case never registers/unregisters own snapshot. Snapshot
1241 * is taken from parallel heap scan, and is SnapshotAny or an MVCC
1242 * snapshot, based on same criteria as serial case.
1243 */
1246 snapshot = scan->rs_snapshot;
1247 }
1248
1249 hscan = (HeapScanDesc) scan;
1250
1251 /*
1252 * Must have called GetOldestNonRemovableTransactionId() if using
1253 * SnapshotAny. Shouldn't have for an MVCC snapshot. (It's especially
1254 * worth checking this for parallel builds, since ambuild routines that
1255 * support parallel builds must work these details out for themselves.)
1256 */
1257 Assert(snapshot == SnapshotAny || IsMVCCSnapshot(snapshot));
1258 Assert(snapshot == SnapshotAny ? TransactionIdIsValid(OldestXmin) :
1259 !TransactionIdIsValid(OldestXmin));
1260 Assert(snapshot == SnapshotAny || !anyvisible);
1261
1262 /* Publish number of blocks to scan */
1263 if (progress)
1264 {
1265 BlockNumber nblocks;
1266
1267 if (hscan->rs_base.rs_parallel != NULL)
1268 {
1270
1271 pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1272 nblocks = pbscan->phs_nblocks;
1273 }
1274 else
1275 nblocks = hscan->rs_nblocks;
1276
1278 nblocks);
1279 }
1280
1281 /* set our scan endpoints */
1282 if (!allow_sync)
1284 else
1285 {
1286 /* syncscan can only be requested on whole relation */
1287 Assert(start_blockno == 0);
1289 }
1290
1291 reltuples = 0;
1292
1293 /*
1294 * Scan all tuples in the base relation.
1295 */
1296 while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1297 {
1298 bool tupleIsAlive;
1299
1301
1302 /* Report scan progress, if asked to. */
1303 if (progress)
1304 {
1306
1308 {
1310 blocks_done);
1312 }
1313 }
1314
1315 /*
1316 * When dealing with a HOT-chain of updated tuples, we want to index
1317 * the values of the live tuple (if any), but index it under the TID
1318 * of the chain's root tuple. This approach is necessary to preserve
1319 * the HOT-chain structure in the heap. So we need to be able to find
1320 * the root item offset for every tuple that's in a HOT-chain. When
1321 * first reaching a new page of the relation, call
1322 * heap_get_root_tuples() to build a map of root item offsets on the
1323 * page.
1324 *
1325 * It might look unsafe to use this information across buffer
1326 * lock/unlock. However, we hold ShareLock on the table so no
1327 * ordinary insert/update/delete should occur; and we hold pin on the
1328 * buffer continuously while visiting the page, so no pruning
1329 * operation can occur either.
1330 *
1331 * In cases with only ShareUpdateExclusiveLock on the table, it's
1332 * possible for some HOT tuples to appear that we didn't know about
1333 * when we first read the page. To handle that case, we re-obtain the
1334 * list of root offsets when a HOT tuple points to a root item that we
1335 * don't know about.
1336 *
1337 * Also, although our opinions about tuple liveness could change while
1338 * we scan the page (due to concurrent transaction commits/aborts),
1339 * the chain root locations won't, so this info doesn't need to be
1340 * rebuilt after waiting for another transaction.
1341 *
1342 * Note the implied assumption that there is no more than one live
1343 * tuple per HOT-chain --- else we could create more than one index
1344 * entry pointing to the same root tuple.
1345 */
1346 if (hscan->rs_cblock != root_blkno)
1347 {
1348 Page page = BufferGetPage(hscan->rs_cbuf);
1349
1353
1354 root_blkno = hscan->rs_cblock;
1355 }
1356
1357 if (snapshot == SnapshotAny)
1358 {
1359 /* do our own time qual check */
1360 bool indexIt;
1362
1363 recheck:
1364
1365 /*
1366 * We could possibly get away with not locking the buffer here,
1367 * since caller should hold ShareLock on the relation, but let's
1368 * be conservative about it. (This remark is still correct even
1369 * with HOT-pruning: our pin on the buffer prevents pruning.)
1370 */
1372
1373 /*
1374 * The criteria for counting a tuple as live in this block need to
1375 * match what analyze.c's heapam_scan_analyze_next_tuple() does,
1376 * otherwise CREATE INDEX and ANALYZE may produce wildly different
1377 * reltuples values, e.g. when there are many recently-dead
1378 * tuples.
1379 */
1380 switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin,
1381 hscan->rs_cbuf))
1382 {
1383 case HEAPTUPLE_DEAD:
1384 /* Definitely dead, we can ignore it */
1385 indexIt = false;
1386 tupleIsAlive = false;
1387 break;
1388 case HEAPTUPLE_LIVE:
1389 /* Normal case, index and unique-check it */
1390 indexIt = true;
1391 tupleIsAlive = true;
1392 /* Count it as live, too */
1393 reltuples += 1;
1394 break;
1396
1397 /*
1398 * If tuple is recently deleted then we must index it
1399 * anyway to preserve MVCC semantics. (Pre-existing
1400 * transactions could try to use the index after we finish
1401 * building it, and may need to see such tuples.)
1402 *
1403 * However, if it was HOT-updated then we must only index
1404 * the live tuple at the end of the HOT-chain. Since this
1405 * breaks semantics for pre-existing snapshots, mark the
1406 * index as unusable for them.
1407 *
1408 * We don't count recently-dead tuples in reltuples, even
1409 * if we index them; see heapam_scan_analyze_next_tuple().
1410 */
1412 {
1413 indexIt = false;
1414 /* mark the index as unsafe for old snapshots */
1415 indexInfo->ii_BrokenHotChain = true;
1416 }
1417 else
1418 indexIt = true;
1419 /* In any case, exclude the tuple from unique-checking */
1420 tupleIsAlive = false;
1421 break;
1423
1424 /*
1425 * In "anyvisible" mode, this tuple is visible and we
1426 * don't need any further checks.
1427 */
1428 if (anyvisible)
1429 {
1430 indexIt = true;
1431 tupleIsAlive = true;
1432 reltuples += 1;
1433 break;
1434 }
1435
1436 /*
1437 * Since caller should hold ShareLock or better, normally
1438 * the only way to see this is if it was inserted earlier
1439 * in our own transaction. However, it can happen in
1440 * system catalogs, since we tend to release write lock
1441 * before commit there. Give a warning if neither case
1442 * applies.
1443 */
1446 {
1447 if (!is_system_catalog)
1448 elog(WARNING, "concurrent insert in progress within table \"%s\"",
1449 RelationGetRelationName(heapRelation));
1450
1451 /*
1452 * If we are performing uniqueness checks, indexing
1453 * such a tuple could lead to a bogus uniqueness
1454 * failure. In that case we wait for the inserting
1455 * transaction to finish and check again.
1456 */
1458 {
1459 /*
1460 * Must drop the lock on the buffer before we wait
1461 */
1463 XactLockTableWait(xwait, heapRelation,
1464 &heapTuple->t_self,
1467 goto recheck;
1468 }
1469 }
1470 else
1471 {
1472 /*
1473 * For consistency with
1474 * heapam_scan_analyze_next_tuple(), count
1475 * HEAPTUPLE_INSERT_IN_PROGRESS tuples as live only
1476 * when inserted by our own transaction.
1477 */
1478 reltuples += 1;
1479 }
1480
1481 /*
1482 * We must index such tuples, since if the index build
1483 * commits then they're good.
1484 */
1485 indexIt = true;
1486 tupleIsAlive = true;
1487 break;
1489
1490 /*
1491 * As with INSERT_IN_PROGRESS case, this is unexpected
1492 * unless it's our own deletion or a system catalog; but
1493 * in anyvisible mode, this tuple is visible.
1494 */
1495 if (anyvisible)
1496 {
1497 indexIt = true;
1498 tupleIsAlive = false;
1499 reltuples += 1;
1500 break;
1501 }
1502
1505 {
1506 if (!is_system_catalog)
1507 elog(WARNING, "concurrent delete in progress within table \"%s\"",
1508 RelationGetRelationName(heapRelation));
1509
1510 /*
1511 * If we are performing uniqueness checks, assuming
1512 * the tuple is dead could lead to missing a
1513 * uniqueness violation. In that case we wait for the
1514 * deleting transaction to finish and check again.
1515 *
1516 * Also, if it's a HOT-updated tuple, we should not
1517 * index it but rather the live tuple at the end of
1518 * the HOT-chain. However, the deleting transaction
1519 * could abort, possibly leaving this tuple as live
1520 * after all, in which case it has to be indexed. The
1521 * only way to know what to do is to wait for the
1522 * deleting transaction to finish and check again.
1523 */
1524 if (checking_uniqueness ||
1526 {
1527 /*
1528 * Must drop the lock on the buffer before we wait
1529 */
1531 XactLockTableWait(xwait, heapRelation,
1532 &heapTuple->t_self,
1535 goto recheck;
1536 }
1537
1538 /*
1539 * Otherwise index it but don't check for uniqueness,
1540 * the same as a RECENTLY_DEAD tuple.
1541 */
1542 indexIt = true;
1543
1544 /*
1545 * Count HEAPTUPLE_DELETE_IN_PROGRESS tuples as live,
1546 * if they were not deleted by the current
1547 * transaction. That's what
1548 * heapam_scan_analyze_next_tuple() does, and we want
1549 * the behavior to be consistent.
1550 */
1551 reltuples += 1;
1552 }
1554 {
1555 /*
1556 * It's a HOT-updated tuple deleted by our own xact.
1557 * We can assume the deletion will commit (else the
1558 * index contents don't matter), so treat the same as
1559 * RECENTLY_DEAD HOT-updated tuples.
1560 */
1561 indexIt = false;
1562 /* mark the index as unsafe for old snapshots */
1563 indexInfo->ii_BrokenHotChain = true;
1564 }
1565 else
1566 {
1567 /*
1568 * It's a regular tuple deleted by our own xact. Index
1569 * it, but don't check for uniqueness nor count in
1570 * reltuples, the same as a RECENTLY_DEAD tuple.
1571 */
1572 indexIt = true;
1573 }
1574 /* In any case, exclude the tuple from unique-checking */
1575 tupleIsAlive = false;
1576 break;
1577 default:
1578 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1579 indexIt = tupleIsAlive = false; /* keep compiler quiet */
1580 break;
1581 }
1582
1584
1585 if (!indexIt)
1586 continue;
1587 }
1588 else
1589 {
1590 /* heap_getnext did the time qual check */
1591 tupleIsAlive = true;
1592 reltuples += 1;
1593 }
1594
1596
1597 /* Set up for predicate or expression evaluation */
1598 ExecStoreBufferHeapTuple(heapTuple, slot, hscan->rs_cbuf);
1599
1600 /*
1601 * In a partial index, discard tuples that don't satisfy the
1602 * predicate.
1603 */
1604 if (predicate != NULL)
1605 {
1606 if (!ExecQual(predicate, econtext))
1607 continue;
1608 }
1609
1610 /*
1611 * For the current heap tuple, extract all the attributes we use in
1612 * this index, and note which are null. This also performs evaluation
1613 * of any expressions needed.
1614 */
1615 FormIndexDatum(indexInfo,
1616 slot,
1617 estate,
1618 values,
1619 isnull);
1620
1621 /*
1622 * You'd think we should go ahead and build the index tuple here, but
1623 * some index AMs want to do further processing on the data first. So
1624 * pass the values[] and isnull[] arrays, instead.
1625 */
1626
1628 {
1629 /*
1630 * For a heap-only tuple, pretend its TID is that of the root. See
1631 * src/backend/access/heap/README.HOT for discussion.
1632 */
1633 ItemPointerData tid;
1634 OffsetNumber offnum;
1635
1636 offnum = ItemPointerGetOffsetNumber(&heapTuple->t_self);
1637
1638 /*
1639 * If a HOT tuple points to a root that we don't know about,
1640 * obtain root items afresh. If that still fails, report it as
1641 * corruption.
1642 */
1643 if (root_offsets[offnum - 1] == InvalidOffsetNumber)
1644 {
1645 Page page = BufferGetPage(hscan->rs_cbuf);
1646
1650 }
1651
1652 if (!OffsetNumberIsValid(root_offsets[offnum - 1]))
1653 ereport(ERROR,
1655 errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1657 offnum,
1658 RelationGetRelationName(heapRelation))));
1659
1661 root_offsets[offnum - 1]);
1662
1663 /* Call the AM's callback routine to process the tuple */
1664 callback(indexRelation, &tid, values, isnull, tupleIsAlive,
1665 callback_state);
1666 }
1667 else
1668 {
1669 /* Call the AM's callback routine to process the tuple */
1670 callback(indexRelation, &heapTuple->t_self, values, isnull,
1671 tupleIsAlive, callback_state);
1672 }
1673 }
1674
1675 /* Report scan progress one last time. */
1676 if (progress)
1677 {
1679
1680 if (hscan->rs_base.rs_parallel != NULL)
1681 {
1683
1684 pbscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1685 blks_done = pbscan->phs_nblocks;
1686 }
1687 else
1688 blks_done = hscan->rs_nblocks;
1689
1691 blks_done);
1692 }
1693
1694 table_endscan(scan);
1695
1696 /* we can now forget our snapshot, if set and registered by us */
1698 UnregisterSnapshot(snapshot);
1699
1701
1702 FreeExecutorState(estate);
1703
1704 /* These may have been pointing to the now-gone estate */
1705 indexInfo->ii_ExpressionsState = NIL;
1706 indexInfo->ii_PredicateState = NULL;
1707
1708 return reltuples;
1709}
void pgstat_progress_update_param(int index, int64 val)
uint32 TransactionId
Definition c.h:795
#define OidIsValid(objectId)
Definition c.h:917
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
int errcode(int sqlerrcode)
Definition elog.c:875
int int errmsg_internal(const char *fmt,...) pg_attribute_printf(1
#define WARNING
Definition elog.h:37
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
ExprState * ExecPrepareQual(List *qual, EState *estate)
Definition execExpr.c:793
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
void FreeExecutorState(EState *estate)
Definition execUtils.c:197
EState * CreateExecutorState(void)
Definition execUtils.c:90
#define GetPerTupleExprContext(estate)
Definition executor.h:665
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:527
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1435
void heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks)
Definition heapam.c:502
@ HEAPTUPLE_RECENTLY_DEAD
Definition heapam.h:140
@ HEAPTUPLE_INSERT_IN_PROGRESS
Definition heapam.h:141
@ HEAPTUPLE_LIVE
Definition heapam.h:139
@ HEAPTUPLE_DELETE_IN_PROGRESS
Definition heapam.h:142
@ HEAPTUPLE_DEAD
Definition heapam.h:138
static BlockNumber heapam_scan_get_blocks_done(HeapScanDesc hscan)
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
static bool HeapTupleIsHotUpdated(const HeapTupleData *tuple)
static bool HeapTupleIsHeapOnly(const HeapTupleData *tuple)
static TransactionId HeapTupleHeaderGetUpdateXid(const HeapTupleHeaderData *tup)
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition index.c:2748
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition itemptr.h:103
void XactLockTableWait(TransactionId xid, Relation rel, const ItemPointerData *ctid, XLTW_Oper oper)
Definition lmgr.c:663
@ XLTW_InsertIndexUnique
Definition lmgr.h:32
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
#define IsBootstrapProcessingMode()
Definition miscadmin.h:486
#define InvalidOffsetNumber
Definition off.h:26
#define ERRCODE_DATA_CORRUPTED
#define INDEX_MAX_KEYS
#define NIL
Definition pg_list.h:68
static int progress
Definition pgbench.c:262
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition procarray.c:1944
#define PROGRESS_SCAN_BLOCKS_DONE
Definition progress.h:151
#define PROGRESS_SCAN_BLOCKS_TOTAL
Definition progress.h:150
void heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
Definition pruneheap.c:2309
#define RelationGetRelationName(relation)
Definition rel.h:550
struct ParallelBlockTableScanDescData * ParallelBlockTableScanDesc
Definition relscan.h:109
@ ForwardScanDirection
Definition sdir.h:28
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void UnregisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:866
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:824
#define SnapshotAny
Definition snapmgr.h:33
#define IsMVCCSnapshot(snapshot)
Definition snapmgr.h:59
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:295
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
bool ii_Unique
Definition execnodes.h:214
bool ii_BrokenHotChain
Definition execnodes.h:226
ExprState * ii_PredicateState
Definition execnodes.h:199
Oid * ii_ExclusionOps
Definition execnodes.h:202
bool ii_Concurrent
Definition execnodes.h:224
List * ii_ExpressionsState
Definition execnodes.h:194
List * ii_Predicate
Definition execnodes.h:197
Form_pg_class rd_rel
Definition rel.h:111
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition tableam.c:92
static void table_endscan(TableScanDesc scan)
Definition tableam.h:1061
static TableScanDesc table_beginscan_strat(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key, bool allow_strat, bool allow_sync)
Definition tableam.h:968
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
#define InvalidTransactionId
Definition transam.h:31
#define TransactionIdIsValid(xid)
Definition transam.h:41
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition xact.c:943

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(), fb(), 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(), progress, PROGRESS_SCAN_BLOCKS_DONE, PROGRESS_SCAN_BLOCKS_TOTAL, RelationData::rd_rel, RegisterSnapshot(), RelationGetRelationName, TableScanDescData::rs_snapshot, SnapshotAny, table_beginscan_strat(), table_endscan(), table_slot_create(), TransactionIdIsCurrentTransactionId(), TransactionIdIsValid, UnregisterSnapshot(), values, WARNING, XactLockTableWait(), and XLTW_InsertIndexUnique.

◆ heapam_index_validate_scan()

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

Definition at line 1712 of file heapam_handler.c.

1717{
1718 TableScanDesc scan;
1722 bool isnull[INDEX_MAX_KEYS];
1724 TupleTableSlot *slot;
1725 EState *estate;
1726 ExprContext *econtext;
1727 BlockNumber root_blkno = InvalidBlockNumber;
1731
1732 /* state variables for the merge */
1735 bool tuplesort_empty = false;
1736
1737 /*
1738 * sanity checks
1739 */
1740 Assert(OidIsValid(indexRelation->rd_rel->relam));
1741
1742 /*
1743 * Need an EState for evaluation of index expressions and partial-index
1744 * predicates. Also a slot to hold the current tuple.
1745 */
1746 estate = CreateExecutorState();
1747 econtext = GetPerTupleExprContext(estate);
1748 slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
1750
1751 /* Arrange for econtext's scan tuple to be the tuple under test */
1752 econtext->ecxt_scantuple = slot;
1753
1754 /* Set up execution state for predicate, if any. */
1755 predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
1756
1757 /*
1758 * Prepare for scan of the base relation. We need just those tuples
1759 * satisfying the passed-in reference snapshot. We must disable syncscan
1760 * here, because it's critical that we read from block zero forward to
1761 * match the sorted TIDs.
1762 */
1763 scan = table_beginscan_strat(heapRelation, /* relation */
1764 snapshot, /* snapshot */
1765 0, /* number of keys */
1766 NULL, /* scan key */
1767 true, /* buffer access strategy OK */
1768 false); /* syncscan not OK */
1769 hscan = (HeapScanDesc) scan;
1770
1772 hscan->rs_nblocks);
1773
1774 /*
1775 * Scan all tuples matching the snapshot.
1776 */
1777 while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1778 {
1779 ItemPointer heapcursor = &heapTuple->t_self;
1782
1784
1785 state->htups += 1;
1786
1788 (hscan->rs_cblock != previous_blkno))
1789 {
1791 hscan->rs_cblock);
1792 previous_blkno = hscan->rs_cblock;
1793 }
1794
1795 /*
1796 * As commented in table_index_build_scan, we should index heap-only
1797 * tuples under the TIDs of their root tuples; so when we advance onto
1798 * a new heap page, build a map of root item offsets on the page.
1799 *
1800 * This complicates merging against the tuplesort output: we will
1801 * visit the live tuples in order by their offsets, but the root
1802 * offsets that we need to compare against the index contents might be
1803 * ordered differently. So we might have to "look back" within the
1804 * tuplesort output, but only within the current page. We handle that
1805 * by keeping a bool array in_index[] showing all the
1806 * already-passed-over tuplesort output TIDs of the current page. We
1807 * clear that array here, when advancing onto a new heap page.
1808 */
1809 if (hscan->rs_cblock != root_blkno)
1810 {
1811 Page page = BufferGetPage(hscan->rs_cbuf);
1812
1816
1817 memset(in_index, 0, sizeof(in_index));
1818
1819 root_blkno = hscan->rs_cblock;
1820 }
1821
1822 /* Convert actual tuple TID to root TID */
1825
1827 {
1830 ereport(ERROR,
1832 errmsg_internal("failed to find parent tuple for heap-only tuple at (%u,%u) in table \"%s\"",
1835 RelationGetRelationName(heapRelation))));
1837 }
1838
1839 /*
1840 * "merge" by skipping through the index tuples until we find or pass
1841 * the current root tuple.
1842 */
1843 while (!tuplesort_empty &&
1844 (!indexcursor ||
1846 {
1847 Datum ts_val;
1848 bool ts_isnull;
1849
1850 if (indexcursor)
1851 {
1852 /*
1853 * Remember index items seen earlier on the current heap page
1854 */
1855 if (ItemPointerGetBlockNumber(indexcursor) == root_blkno)
1857 }
1858
1859 tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true,
1860 false, &ts_val, &ts_isnull,
1861 NULL);
1863 if (!tuplesort_empty)
1864 {
1867 }
1868 else
1869 {
1870 /* Be tidy */
1871 indexcursor = NULL;
1872 }
1873 }
1874
1875 /*
1876 * If the tuplesort has overshot *and* we didn't see a match earlier,
1877 * then this tuple is missing from the index, so insert it.
1878 */
1879 if ((tuplesort_empty ||
1881 !in_index[root_offnum - 1])
1882 {
1884
1885 /* Set up for predicate or expression evaluation */
1886 ExecStoreHeapTuple(heapTuple, slot, false);
1887
1888 /*
1889 * In a partial index, discard tuples that don't satisfy the
1890 * predicate.
1891 */
1892 if (predicate != NULL)
1893 {
1894 if (!ExecQual(predicate, econtext))
1895 continue;
1896 }
1897
1898 /*
1899 * For the current heap tuple, extract all the attributes we use
1900 * in this index, and note which are null. This also performs
1901 * evaluation of any expressions needed.
1902 */
1903 FormIndexDatum(indexInfo,
1904 slot,
1905 estate,
1906 values,
1907 isnull);
1908
1909 /*
1910 * You'd think we should go ahead and build the index tuple here,
1911 * but some index AMs want to do further processing on the data
1912 * first. So pass the values[] and isnull[] arrays, instead.
1913 */
1914
1915 /*
1916 * If the tuple is already committed dead, you might think we
1917 * could suppress uniqueness checking, but this is no longer true
1918 * in the presence of HOT, because the insert is actually a proxy
1919 * for a uniqueness check on the whole HOT-chain. That is, the
1920 * tuple we have here could be dead because it was already
1921 * HOT-updated, and if so the updating transaction will not have
1922 * thought it should insert index entries. The index AM will
1923 * check the whole HOT-chain and correctly detect a conflict if
1924 * there is one.
1925 */
1926
1927 index_insert(indexRelation,
1928 values,
1929 isnull,
1930 &rootTuple,
1931 heapRelation,
1932 indexInfo->ii_Unique ?
1934 false,
1935 indexInfo);
1936
1937 state->tups_inserted += 1;
1938 }
1939 }
1940
1941 table_endscan(scan);
1942
1944
1945 FreeExecutorState(estate);
1946
1947 /* These may have been pointing to the now-gone estate */
1948 indexInfo->ii_ExpressionsState = NIL;
1949 indexInfo->ii_PredicateState = NULL;
1950}
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsHeapTuple
Definition execTuples.c:85
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
@ UNIQUE_CHECK_NO
Definition genam.h:125
@ UNIQUE_CHECK_YES
Definition genam.h:126
#define false
static void itemptr_decode(ItemPointer itemptr, int64 encoded)
Definition index.h:218
bool index_insert(Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_t_ctid, Relation heapRelation, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition indexam.c:214
int32 ItemPointerCompare(const ItemPointerData *arg1, const ItemPointerData *arg2)
Definition itemptr.c:51
static void ItemPointerSetOffsetNumber(ItemPointerData *pointer, OffsetNumber offsetNumber)
Definition itemptr.h:158
static int64 DatumGetInt64(Datum X)
Definition postgres.h:416
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(), fb(), 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, 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 535 of file heapam_handler.c.

536{
538
539 /*
540 * Since we copy the file directly without looking at the shared buffers,
541 * we'd better first flush out any pages of the source relation that are
542 * in shared buffers. We assume no new changes will be made while we are
543 * holding exclusive lock on the rel.
544 */
546
547 /*
548 * Create and copy all forks of the relation, and schedule unlinking of
549 * old physical files.
550 *
551 * NOTE: any conflict in relfilenumber value will be caught in
552 * RelationCreateStorage().
553 */
554 dstrel = RelationCreateStorage(*newrlocator, rel->rd_rel->relpersistence, true);
555
556 /* copy main fork */
558 rel->rd_rel->relpersistence);
559
560 /* copy those extra forks that exist */
561 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
562 forkNum <= MAX_FORKNUM; forkNum++)
563 {
564 if (smgrexists(RelationGetSmgr(rel), forkNum))
565 {
566 smgrcreate(dstrel, forkNum, false);
567
568 /*
569 * WAL log creation if the relation is persistent, or this is the
570 * init fork of an unlogged relation.
571 */
572 if (RelationIsPermanent(rel) ||
573 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
574 forkNum == INIT_FORKNUM))
575 log_smgrcreate(newrlocator, forkNum);
577 rel->rd_rel->relpersistence);
578 }
579 }
580
581
582 /* drop old relation, and close new one */
585}
void FlushRelationBuffers(Relation rel)
Definition bufmgr.c:5185
static SMgrRelation RelationGetSmgr(Relation rel)
Definition rel.h:578
#define RelationIsPermanent(relation)
Definition rel.h:628
ForkNumber
Definition relpath.h:56
@ INIT_FORKNUM
Definition relpath.h:61
#define MAX_FORKNUM
Definition relpath.h:70
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition smgr.c:481
void smgrclose(SMgrRelation reln)
Definition smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition smgr.c:462
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition storage.c:187
void RelationDropStorage(Relation rel)
Definition storage.c:207

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

◆ heapam_relation_copy_for_cluster()

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

Definition at line 588 of file heapam_handler.c.

597{
599 BulkInsertState bistate;
604 Tuplesortstate *tuplesort;
607 TupleTableSlot *slot;
608 int natts;
609 Datum *values;
610 bool *isnull;
613 bool concurrent = snapshot != NULL;
614
615 /* Remember if it's a system catalog */
617
618 /*
619 * Valid smgr_targblock implies something already wrote to the relation.
620 * This may be harmless, but this function hasn't planned for it.
621 */
623
624 /* Preallocate values/isnull arrays */
625 natts = newTupDesc->natts;
626 values = palloc_array(Datum, natts);
627 isnull = palloc_array(bool, natts);
628
629 /*
630 * In non-concurrent mode, initialize the rewrite operation. This is not
631 * needed in concurrent mode.
632 */
633 if (!concurrent)
636 else
637 rwstate = NULL;
638
639 /* In concurrent mode, prepare for bulk-insert operation. */
640 if (concurrent)
641 bistate = GetBulkInsertState();
642 else
643 bistate = NULL;
644
645 /* Set up sorting if wanted */
646 if (use_sort)
650 else
651 tuplesort = NULL;
652
653 /*
654 * Prepare to scan the OldHeap. To ensure we see recently-dead tuples
655 * that still need to be copied, we scan with SnapshotAny and use
656 * HeapTupleSatisfiesVacuum for the visibility test.
657 *
658 * In the CONCURRENTLY case, we do regular MVCC visibility tests, using
659 * the snapshot passed by the caller.
660 */
661 if (OldIndex != NULL && !use_sort)
662 {
663 const int ci_index[] = {
666 };
667 int64 ci_val[2];
668
669 /* Set phase and OIDOldIndex to columns */
673
674 tableScan = NULL;
675 heapScan = NULL;
677 snapshot ? snapshot : SnapshotAny,
678 NULL, 0, 0,
679 SO_NONE);
681 }
682 else
683 {
684 /* In scan-and-sort mode and also VACUUM FULL, set phase */
687
689 snapshot ? snapshot : SnapshotAny,
690 0, (ScanKey) NULL,
691 SO_NONE);
693 indexScan = NULL;
694
695 /* Set total heap blocks */
697 heapScan->rs_nblocks);
698 }
699
702
703 /*
704 * Scan through the OldHeap, either in OldIndex order or sequentially;
705 * copy each tuple into the NewHeap, or transiently to the tuplesort
706 * module. Note that we don't bother sorting dead tuples (they won't get
707 * to the new table anyway).
708 */
709 for (;;)
710 {
711 HeapTuple tuple;
712 Buffer buf;
713 bool isdead;
714
716
717 if (indexScan != NULL)
718 {
720 break;
721
722 /* Since we used no scan keys, should never need to recheck */
723 if (indexScan->xs_recheck)
724 elog(ERROR, "CLUSTER does not support lossy index conditions");
725 }
726 else
727 {
729 {
730 /*
731 * If the last pages of the scan were empty, we would go to
732 * the next phase while heap_blks_scanned != heap_blks_total.
733 * Instead, to ensure that heap_blks_scanned is equivalent to
734 * heap_blks_total after the table scan phase, this parameter
735 * is manually updated to the correct value when the table
736 * scan finishes.
737 */
739 heapScan->rs_nblocks);
740 break;
741 }
742
743 /*
744 * In scan-and-sort mode and also VACUUM FULL, set heap blocks
745 * scanned
746 *
747 * Note that heapScan may start at an offset and wrap around, i.e.
748 * rs_startblock may be >0, and rs_cblock may end with a number
749 * below rs_startblock. To prevent showing this wraparound to the
750 * user, we offset rs_cblock by rs_startblock (modulo rs_nblocks).
751 */
752 if (prev_cblock != heapScan->rs_cblock)
753 {
755 (heapScan->rs_cblock +
756 heapScan->rs_nblocks -
757 heapScan->rs_startblock
758 ) % heapScan->rs_nblocks + 1);
759 prev_cblock = heapScan->rs_cblock;
760 }
761 }
762
763 tuple = ExecFetchSlotHeapTuple(slot, false, NULL);
764 buf = hslot->buffer;
765
766 /*
767 * In concurrent mode, our table or index scan has used regular MVCC
768 * visibility test against a snapshot passed by caller; therefore we
769 * don't need another visibility test. In non-concurrent mode
770 * however, we must test the visibility of each tuple we read.
771 */
772 if (!concurrent)
773 {
774 /*
775 * To be able to guarantee that we can set the hint bit, acquire
776 * an exclusive lock on the old buffer. We need the hint bits, set
777 * in heapam_relation_copy_for_cluster() ->
778 * HeapTupleSatisfiesVacuum(), to be set, as otherwise
779 * reform_and_rewrite_tuple() -> rewrite_heap_tuple() will get
780 * confused. Specifically, rewrite_heap_tuple() checks for
781 * HEAP_XMAX_INVALID in the old tuple to determine whether to
782 * check the old-to-new mapping hash table.
783 *
784 * It'd be better if we somehow could avoid setting hint bits on
785 * the old page. One reason to use VACUUM FULL are very bloated
786 * tables - rewriting most of the old table during VACUUM FULL
787 * doesn't exactly help...
788 */
790
791 switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf))
792 {
793 case HEAPTUPLE_DEAD:
794 /* Definitely dead */
795 isdead = true;
796 break;
798 *tups_recently_dead += 1;
800 case HEAPTUPLE_LIVE:
801 /* Live or recently dead, must copy it */
802 isdead = false;
803 break;
805
806 /*
807 * As long as we hold exclusive lock on the relation,
808 * normally the only way to see this is if it was inserted
809 * earlier in our own transaction. However, it can happen
810 * in system catalogs, since we tend to release write lock
811 * before commit there. Give a warning if neither case
812 * applies; but in any case we had better copy it.
813 */
814 if (!is_system_catalog &&
816 elog(WARNING, "concurrent insert in progress within table \"%s\"",
818 /* treat as live */
819 isdead = false;
820 break;
822
823 /*
824 * Similar situation to INSERT_IN_PROGRESS case.
825 */
826 if (!is_system_catalog &&
828 elog(WARNING, "concurrent delete in progress within table \"%s\"",
830 /* treat as recently dead */
831 *tups_recently_dead += 1;
832 isdead = false;
833 break;
834 default:
835 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
836 isdead = false; /* keep compiler quiet */
837 break;
838 }
839
841
842 if (isdead)
843 {
844 *tups_vacuumed += 1;
845 /* heap rewrite module still needs to see it... */
847 {
848 /* A previous recently-dead tuple is now known dead */
849 *tups_vacuumed += 1;
850 *tups_recently_dead -= 1;
851 }
852
853 continue;
854 }
855 }
856
857 *num_tuples += 1;
858 if (tuplesort != NULL)
859 {
860 tuplesort_putheaptuple(tuplesort, tuple);
861
862 /*
863 * In scan-and-sort mode, report increase in number of tuples
864 * scanned
865 */
867 *num_tuples);
868 }
869 else
870 {
871 const int ct_index[] = {
874 };
875 int64 ct_val[2];
876
877 if (!concurrent)
879 values, isnull, rwstate);
880 else
882 values, isnull, bistate);
883
884 /*
885 * In indexscan mode and also VACUUM FULL, report increase in
886 * number of tuples scanned and written
887 */
888 ct_val[0] = *num_tuples;
889 ct_val[1] = *num_tuples;
891 }
892 }
893
894 if (indexScan != NULL)
896 if (tableScan != NULL)
898 if (slot)
900
901 /*
902 * In scan-and-sort mode, complete the sort, then read out all live tuples
903 * from the tuplestore and write them to the new relation.
904 */
905 if (tuplesort != NULL)
906 {
907 double n_tuples = 0;
908
909 /* Report that we are now sorting tuples */
912
913 tuplesort_performsort(tuplesort);
914
915 /* Report that we are now writing new heap */
918
919 for (;;)
920 {
921 HeapTuple tuple;
922
924
925 tuple = tuplesort_getheaptuple(tuplesort, true);
926 if (tuple == NULL)
927 break;
928
929 n_tuples += 1;
930 if (!concurrent)
933 values, isnull,
934 rwstate);
935 else
937 values, isnull, bistate);
938
939 /* Report n_tuples */
941 n_tuples);
942 }
943
944 tuplesort_end(tuplesort);
945 }
946
947 /* Write out any remaining tuples, and fsync if needed */
948 if (rwstate)
950 if (bistate)
951 FreeBulkInsertState(bistate);
952
953 /* Clean up */
954 pfree(values);
955 pfree(isnull);
956}
void pgstat_progress_update_multi_param(int nparam, const int *index, const int64 *val)
@ BUFFER_LOCK_EXCLUSIVE
Definition bufmgr.h:222
int64_t int64
Definition c.h:680
#define pg_fallthrough
Definition c.h:220
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
#define palloc_array(type, count)
Definition fe_memutils.h:91
int maintenance_work_mem
Definition globals.c:135
BulkInsertState GetBulkInsertState(void)
Definition heapam.c:1937
void FreeBulkInsertState(BulkInsertState bistate)
Definition heapam.c:1954
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
Definition indexam.c:698
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, IndexScanInstrumentation *instrument, int nkeys, int norderbys, uint32 flags)
Definition indexam.c:257
void index_endscan(IndexScanDesc scan)
Definition indexam.c:394
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition indexam.c:368
void pfree(void *pointer)
Definition mcxt.c:1619
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define PROGRESS_REPACK_TOTAL_HEAP_BLKS
Definition progress.h:92
#define PROGRESS_REPACK_PHASE
Definition progress.h:86
#define PROGRESS_REPACK_PHASE_WRITE_NEW_HEAP
Definition progress.h:102
#define PROGRESS_REPACK_HEAP_TUPLES_SCANNED
Definition progress.h:88
#define PROGRESS_REPACK_PHASE_SEQ_SCAN_HEAP
Definition progress.h:99
#define PROGRESS_REPACK_PHASE_INDEX_SCAN_HEAP
Definition progress.h:100
#define PROGRESS_REPACK_HEAP_BLKS_SCANNED
Definition progress.h:93
#define PROGRESS_REPACK_PHASE_SORT_TUPLES
Definition progress.h:101
#define PROGRESS_REPACK_INDEX_RELID
Definition progress.h:87
#define PROGRESS_REPACK_HEAP_TUPLES_INSERTED
Definition progress.h:89
#define RelationGetTargetBlock(relation)
Definition rel.h:612
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)
@ SO_NONE
Definition tableam.h:49
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key, uint32 flags)
Definition tableam.h:943
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition tableam.h:1096
void tuplesort_performsort(Tuplesortstate *state)
Definition tuplesort.c:1260
void tuplesort_end(Tuplesortstate *state)
Definition tuplesort.c:848
#define TUPLESORT_NONE
Definition tuplesort.h:67
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, BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, CHECK_FOR_INTERRUPTS, elog, end_heap_rewrite(), ERROR, ExecDropSingleTupleTableSlot(), ExecFetchSlotHeapTuple(), fb(), ForwardScanDirection, FreeBulkInsertState(), GetBulkInsertState(), heap_insert_for_repack(), 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, palloc_array, pfree(), pg_fallthrough, pgstat_progress_update_multi_param(), pgstat_progress_update_param(), PROGRESS_REPACK_HEAP_BLKS_SCANNED, PROGRESS_REPACK_HEAP_TUPLES_INSERTED, PROGRESS_REPACK_HEAP_TUPLES_SCANNED, PROGRESS_REPACK_INDEX_RELID, PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PHASE_INDEX_SCAN_HEAP, PROGRESS_REPACK_PHASE_SEQ_SCAN_HEAP, PROGRESS_REPACK_PHASE_SORT_TUPLES, PROGRESS_REPACK_PHASE_WRITE_NEW_HEAP, PROGRESS_REPACK_TOTAL_HEAP_BLKS, reform_and_rewrite_tuple(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, RelationGetTargetBlock, rewrite_heap_dead_tuple(), SnapshotAny, SO_NONE, 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, and WARNING.

◆ heapam_relation_needs_toast_table()

static bool heapam_relation_needs_toast_table ( Relation  rel)
static

Definition at line 2004 of file heapam_handler.c.

2005{
2006 int32 data_length = 0;
2007 bool maxlength_unknown = false;
2008 bool has_toastable_attrs = false;
2009 TupleDesc tupdesc = rel->rd_att;
2011 int i;
2012
2013 for (i = 0; i < tupdesc->natts; i++)
2014 {
2015 Form_pg_attribute att = TupleDescAttr(tupdesc, i);
2016
2017 if (att->attisdropped)
2018 continue;
2019 if (att->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
2020 continue;
2021 data_length = att_align_nominal(data_length, att->attalign);
2022 if (att->attlen > 0)
2023 {
2024 /* Fixed-length types are never toastable */
2025 data_length += att->attlen;
2026 }
2027 else
2028 {
2029 int32 maxlen = type_maximum_size(att->atttypid,
2030 att->atttypmod);
2031
2032 if (maxlen < 0)
2033 maxlength_unknown = true;
2034 else
2035 data_length += maxlen;
2036 if (att->attstorage != TYPSTORAGE_PLAIN)
2037 has_toastable_attrs = true;
2038 }
2039 }
2041 return false; /* nothing to toast? */
2043 return true; /* any unlimited-length attrs? */
2045 BITMAPLEN(tupdesc->natts)) +
2046 MAXALIGN(data_length);
2048}
#define MAXALIGN(LEN)
Definition c.h:955
int32_t int32
Definition c.h:679
int32 type_maximum_size(Oid type_oid, int32 typemod)
#define TOAST_TUPLE_THRESHOLD
Definition heaptoast.h:48
#define SizeofHeapTupleHeader
static int BITMAPLEN(int NATTS)
FormData_pg_attribute * Form_pg_attribute
TupleDesc rd_att
Definition rel.h:112
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
#define att_align_nominal(cur_offset, attalign)
Definition tupmacs.h:404

References att_align_nominal, BITMAPLEN(), fb(), 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 529 of file heapam_handler.c.

530{
531 RelationTruncate(rel, 0);
532}
void RelationTruncate(Relation rel, BlockNumber nblocks)
Definition storage.c:289

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 486 of file heapam_handler.c.

491{
492 SMgrRelation srel;
493
494 /*
495 * Initialize to the minimum XID that could put tuples in the table. We
496 * know that no xacts older than RecentXmin are still running, so that
497 * will do.
498 */
500
501 /*
502 * Similarly, initialize the minimum Multixact to the first value that
503 * could possibly be stored in tuples in the table. Running transactions
504 * could reuse values from their local cache, so we are careful to
505 * consider all currently running multis.
506 *
507 * XXX this could be refined further, but is it worth the hassle?
508 */
510
511 srel = RelationCreateStorage(*newrlocator, persistence, true);
512
513 /*
514 * If required, set up an init fork for an unlogged table so that it can
515 * be correctly reinitialized on restart.
516 */
517 if (persistence == RELPERSISTENCE_UNLOGGED)
518 {
519 Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
520 rel->rd_rel->relkind == RELKIND_TOASTVALUE);
521 smgrcreate(srel, INIT_FORKNUM, false);
523 }
524
525 smgrclose(srel);
526}
MultiXactId GetOldestMultiXactId(void)
Definition multixact.c:2378
TransactionId RecentXmin
Definition snapmgr.c:160

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

◆ heapam_relation_toast_am()

static Oid heapam_relation_toast_am ( Relation  rel)
static

Definition at line 2054 of file heapam_handler.c.

2055{
2056 return rel->rd_rel->relam;
2057}

References RelationData::rd_rel.

◆ heapam_scan_analyze_next_block()

static bool heapam_scan_analyze_next_block ( TableScanDesc  scan,
ReadStream stream 
)
static

Definition at line 968 of file heapam_handler.c.

969{
971
972 /*
973 * We must maintain a pin on the target page's buffer to ensure that
974 * concurrent activity - e.g. HOT pruning - doesn't delete tuples out from
975 * under us. It comes from the stream already pinned. We also choose to
976 * hold sharelock on the buffer throughout --- we could release and
977 * re-acquire sharelock for each tuple, but since we aren't doing much
978 * work per tuple, the extra lock traffic is probably better avoided.
979 */
981 if (!BufferIsValid(hscan->rs_cbuf))
982 return false;
983
985
986 hscan->rs_cblock = BufferGetBlockNumber(hscan->rs_cbuf);
987 hscan->rs_cindex = FirstOffsetNumber;
988 return true;
989}
Buffer rs_cbuf
Definition heapam.h:72

References BUFFER_LOCK_SHARE, BufferGetBlockNumber(), BufferIsValid(), fb(), FirstOffsetNumber, LockBuffer(), and read_stream_next_buffer().

◆ heapam_scan_analyze_next_tuple()

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

Definition at line 992 of file heapam_handler.c.

995{
1000
1002
1004 targpage = BufferGetPage(hscan->rs_cbuf);
1006
1007 /* Inner loop over all tuples on the selected page */
1008 for (; hscan->rs_cindex <= maxoffset; hscan->rs_cindex++)
1009 {
1010 ItemId itemid;
1011 HeapTuple targtuple = &hslot->base.tupdata;
1012 bool sample_it = false;
1014
1015 itemid = PageGetItemId(targpage, hscan->rs_cindex);
1016
1017 /*
1018 * We ignore unused and redirect line pointers. DEAD line pointers
1019 * should be counted as dead, because we need vacuum to run to get rid
1020 * of them. Note that this rule agrees with the way that
1021 * heap_page_prune_and_freeze() counts things.
1022 */
1023 if (!ItemIdIsNormal(itemid))
1024 {
1025 if (ItemIdIsDead(itemid))
1026 *deadrows += 1;
1027 continue;
1028 }
1029
1030 ItemPointerSet(&targtuple->t_self, hscan->rs_cblock, hscan->rs_cindex);
1031
1032 targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
1033 targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
1034 targtuple->t_len = ItemIdGetLength(itemid);
1035
1037 hscan->rs_cbuf,
1038 &dead_after))
1039 {
1040 case HEAPTUPLE_LIVE:
1041 sample_it = true;
1042 *liverows += 1;
1043 break;
1044
1045 case HEAPTUPLE_DEAD:
1047 /* Count dead and recently-dead rows */
1048 *deadrows += 1;
1049 break;
1050
1052
1053 /*
1054 * Insert-in-progress rows are not counted. We assume that
1055 * when the inserting transaction commits or aborts, it will
1056 * send a stats message to increment the proper count. This
1057 * works right only if that transaction ends after we finish
1058 * analyzing the table; if things happen in the other order,
1059 * its stats update will be overwritten by ours. However, the
1060 * error will be large only if the other transaction runs long
1061 * enough to insert many tuples, so assuming it will finish
1062 * after us is the safer option.
1063 *
1064 * A special case is that the inserting transaction might be
1065 * our own. In this case we should count and sample the row,
1066 * to accommodate users who load a table and analyze it in one
1067 * transaction. (pgstat_report_analyze has to adjust the
1068 * numbers we report to the cumulative stats system to make
1069 * this come out right.)
1070 */
1072 {
1073 sample_it = true;
1074 *liverows += 1;
1075 }
1076 break;
1077
1079
1080 /*
1081 * We count and sample delete-in-progress rows the same as
1082 * live ones, so that the stats counters come out right if the
1083 * deleting transaction commits after us, per the same
1084 * reasoning given above.
1085 *
1086 * If the delete was done by our own transaction, however, we
1087 * must count the row as dead to make pgstat_report_analyze's
1088 * stats adjustments come out right. (Note: this works out
1089 * properly when the row was both inserted and deleted in our
1090 * xact.)
1091 *
1092 * The net effect of these choices is that we act as though an
1093 * IN_PROGRESS transaction hasn't happened yet, except if it
1094 * is our own transaction, which we assume has happened.
1095 *
1096 * This approach ensures that we behave sanely if we see both
1097 * the pre-image and post-image rows for a row being updated
1098 * by a concurrent transaction: we will sample the pre-image
1099 * but not the post-image. We also get sane results if the
1100 * concurrent transaction never commits.
1101 */
1103 *deadrows += 1;
1104 else
1105 {
1106 sample_it = true;
1107 *liverows += 1;
1108 }
1109 break;
1110
1111 default:
1112 elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
1113 break;
1114 }
1115
1116 if (sample_it)
1117 {
1118 ExecStoreBufferHeapTuple(targtuple, slot, hscan->rs_cbuf);
1119 hscan->rs_cindex++;
1120
1121 /* note that we leave the buffer locked here! */
1122 return true;
1123 }
1124 }
1125
1126 /* Now release the lock and pin on the page */
1127 UnlockReleaseBuffer(hscan->rs_cbuf);
1128 hscan->rs_cbuf = InvalidBuffer;
1129
1130 /* also prevent old slot contents from having pin on page */
1131 ExecClearTuple(slot);
1132
1133 return false;
1134}
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5626
HTSV_Result HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *dead_after)
#define ItemIdIsDead(itemId)
Definition itemid.h:113

References Assert, BufferGetPage(), elog, ERROR, ExecClearTuple(), ExecStoreBufferHeapTuple(), fb(), HEAPTUPLE_DEAD, HEAPTUPLE_DELETE_IN_PROGRESS, HEAPTUPLE_INSERT_IN_PROGRESS, HEAPTUPLE_LIVE, HEAPTUPLE_RECENTLY_DEAD, HeapTupleHeaderGetUpdateXid(), HeapTupleHeaderGetXmin(), HeapTupleSatisfiesVacuumHorizon(), InvalidBuffer, ItemIdGetLength, ItemIdIsDead, ItemIdIsNormal, ItemPointerSet(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), RelationGetRelid, TableScanDescData::rs_rd, TransactionIdIsCurrentTransactionId(), TTS_IS_BUFFERTUPLE, and UnlockReleaseBuffer().

◆ heapam_scan_bitmap_next_tuple()

static bool heapam_scan_bitmap_next_tuple ( TableScanDesc  scan,
TupleTableSlot slot,
bool recheck,
uint64 lossy_pages,
uint64 exact_pages 
)
static

Definition at line 2088 of file heapam_handler.c.

2093{
2097 Page page;
2098 ItemId lp;
2099
2100 /*
2101 * Out of range? If so, nothing more to look at on this page
2102 */
2103 while (hscan->rs_cindex >= hscan->rs_ntuples)
2104 {
2105 /*
2106 * Returns false if the bitmap is exhausted and there are no further
2107 * blocks we need to scan.
2108 */
2109 if (!BitmapHeapScanNextBlock(scan, recheck, lossy_pages, exact_pages))
2110 return false;
2111 }
2112
2113 targoffset = hscan->rs_vistuples[hscan->rs_cindex];
2114 page = BufferGetPage(hscan->rs_cbuf);
2115 lp = PageGetItemId(page, targoffset);
2117
2118 hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
2119 hscan->rs_ctup.t_len = ItemIdGetLength(lp);
2120 hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
2121 ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);
2122
2124
2125 /*
2126 * Set up the result slot to point to this tuple. Note that the slot
2127 * acquires a pin on the buffer.
2128 */
2130 slot,
2131 hscan->rs_cbuf);
2132
2133 hscan->rs_cindex++;
2134
2135 return true;
2136}

References Assert, BitmapHeapScanNextBlock(), BufferGetPage(), ExecStoreBufferHeapTuple(), fb(), ItemIdGetLength, ItemIdIsNormal, ItemPointerSet(), PageGetItem(), PageGetItemId(), pgstat_count_heap_fetch, RelationData::rd_id, and TableScanDescData::rs_rd.

◆ heapam_scan_get_blocks_done()

static BlockNumber heapam_scan_get_blocks_done ( HeapScanDesc  hscan)
static

Definition at line 1959 of file heapam_handler.c.

1960{
1962 BlockNumber startblock;
1964
1965 if (hscan->rs_base.rs_parallel != NULL)
1966 {
1967 bpscan = (ParallelBlockTableScanDesc) hscan->rs_base.rs_parallel;
1968 startblock = bpscan->phs_startblock;
1969 }
1970 else
1971 startblock = hscan->rs_startblock;
1972
1973 /*
1974 * Might have wrapped around the end of the relation, if startblock was
1975 * not zero.
1976 */
1977 if (hscan->rs_cblock > startblock)
1978 blocks_done = hscan->rs_cblock - startblock;
1979 else
1980 {
1981 BlockNumber nblocks;
1982
1983 nblocks = bpscan != NULL ? bpscan->phs_nblocks : hscan->rs_nblocks;
1984 blocks_done = nblocks - startblock +
1985 hscan->rs_cblock;
1986 }
1987
1988 return blocks_done;
1989}

References fb().

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 2139 of file heapam_handler.c.

2140{
2142 TsmRoutine *tsm = scanstate->tsmroutine;
2143 BlockNumber blockno;
2144
2145 /* return false immediately if relation is empty */
2146 if (hscan->rs_nblocks == 0)
2147 return false;
2148
2149 /* release previous scan buffer, if any */
2150 if (BufferIsValid(hscan->rs_cbuf))
2151 {
2152 ReleaseBuffer(hscan->rs_cbuf);
2153 hscan->rs_cbuf = InvalidBuffer;
2154 }
2155
2156 if (tsm->NextSampleBlock)
2157 blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks);
2158 else
2159 {
2160 /* scanning table sequentially */
2161
2162 if (hscan->rs_cblock == InvalidBlockNumber)
2163 {
2164 Assert(!hscan->rs_inited);
2165 blockno = hscan->rs_startblock;
2166 }
2167 else
2168 {
2169 Assert(hscan->rs_inited);
2170
2171 blockno = hscan->rs_cblock + 1;
2172
2173 if (blockno >= hscan->rs_nblocks)
2174 {
2175 /* wrap to beginning of rel, might not have started at 0 */
2176 blockno = 0;
2177 }
2178
2179 /*
2180 * Report our new scan position for synchronization purposes.
2181 *
2182 * Note: we do this before checking for end of scan so that the
2183 * final state of the position hint is back at the start of the
2184 * rel. That's not strictly necessary, but otherwise when you run
2185 * the same query multiple times the starting position would shift
2186 * a little bit backwards on every invocation, which is confusing.
2187 * We don't guarantee any specific ordering in general, though.
2188 */
2189 if (scan->rs_flags & SO_ALLOW_SYNC)
2190 ss_report_location(scan->rs_rd, blockno);
2191
2192 if (blockno == hscan->rs_startblock)
2193 {
2194 blockno = InvalidBlockNumber;
2195 }
2196 }
2197 }
2198
2199 hscan->rs_cblock = blockno;
2200
2201 if (!BlockNumberIsValid(blockno))
2202 {
2203 hscan->rs_inited = false;
2204 return false;
2205 }
2206
2207 Assert(hscan->rs_cblock < hscan->rs_nblocks);
2208
2209 /*
2210 * Be sure to check for interrupts at least once per page. Checks at
2211 * higher code levels won't be able to stop a sample scan that encounters
2212 * many pages' worth of consecutive dead tuples.
2213 */
2215
2216 /* Read page using selected strategy */
2217 hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
2218 blockno, RBM_NORMAL, hscan->rs_strategy);
2219
2220 /* in pagemode, prune the page and determine visible tuple offsets */
2221 if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
2223
2224 hscan->rs_inited = true;
2225 return true;
2226}

References Assert, BlockNumberIsValid(), BufferIsValid(), CHECK_FOR_INTERRUPTS, fb(), heap_prepare_pagescan(), InvalidBlockNumber, InvalidBuffer, MAIN_FORKNUM, RBM_NORMAL, ReadBufferExtended(), ReleaseBuffer(), TableScanDescData::rs_flags, TableScanDescData::rs_rd, SO_ALLOW_PAGEMODE, SO_ALLOW_SYNC, and ss_report_location().

◆ heapam_scan_sample_next_tuple()

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

Definition at line 2229 of file heapam_handler.c.

2231{
2233 TsmRoutine *tsm = scanstate->tsmroutine;
2234 BlockNumber blockno = hscan->rs_cblock;
2235 bool pagemode = (scan->rs_flags & SO_ALLOW_PAGEMODE) != 0;
2236
2237 Page page;
2238 bool all_visible;
2240
2241 /*
2242 * When not using pagemode, we must lock the buffer during tuple
2243 * visibility checks.
2244 */
2245 if (!pagemode)
2247
2248 page = BufferGetPage(hscan->rs_cbuf);
2249 all_visible = PageIsAllVisible(page) &&
2252
2253 for (;;)
2254 {
2256
2258
2259 /* Ask the tablesample method which tuples to check on this page. */
2260 tupoffset = tsm->NextSampleTuple(scanstate,
2261 blockno,
2262 maxoffset);
2263
2265 {
2266 ItemId itemid;
2267 bool visible;
2268 HeapTuple tuple = &(hscan->rs_ctup);
2269
2270 /* Skip invalid tuple pointers. */
2271 itemid = PageGetItemId(page, tupoffset);
2272 if (!ItemIdIsNormal(itemid))
2273 continue;
2274
2275 tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
2276 tuple->t_len = ItemIdGetLength(itemid);
2277 ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
2278
2279
2280 if (all_visible)
2281 visible = true;
2282 else
2283 visible = SampleHeapTupleVisible(scan, hscan->rs_cbuf,
2284 tuple, tupoffset);
2285
2286 /* in pagemode, heap_prepare_pagescan did this for us */
2287 if (!pagemode)
2288 HeapCheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
2289 hscan->rs_cbuf, scan->rs_snapshot);
2290
2291 /* Try next tuple from same page. */
2292 if (!visible)
2293 continue;
2294
2295 /* Found visible tuple, return it. */
2296 if (!pagemode)
2298
2299 ExecStoreBufferHeapTuple(tuple, slot, hscan->rs_cbuf);
2300
2301 /* Count successfully-fetched tuples as heap fetches */
2303
2304 return true;
2305 }
2306 else
2307 {
2308 /*
2309 * If we get here, it means we've exhausted the items on this page
2310 * and it's time to move to the next.
2311 */
2312 if (!pagemode)
2314
2315 ExecClearTuple(slot);
2316 return false;
2317 }
2318 }
2319
2320 Assert(0);
2321}

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

◆ heapam_slot_callbacks()

static const TupleTableSlotOps * heapam_slot_callbacks ( Relation  relation)
static

Definition at line 77 of file heapam_handler.c.

78{
80}
const TupleTableSlotOps TTSOpsBufferHeapTuple
Definition execTuples.c:87

References TTSOpsBufferHeapTuple.

◆ heapam_tuple_complete_speculative()

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

Definition at line 192 of file heapam_handler.c.

194{
195 /* adjust the tuple's state accordingly */
196 if (succeeded)
197 heap_finish_speculative(relation, &slot->tts_tid);
198 else
199 heap_abort_speculative(relation, &slot->tts_tid);
200}
void heap_finish_speculative(Relation relation, const ItemPointerData *tid)
Definition heapam.c:6063
void heap_abort_speculative(Relation relation, const ItemPointerData *tid)
Definition heapam.c:6150
ItemPointerData tts_tid
Definition tuptable.h:142

References fb(), heap_abort_speculative(), heap_finish_speculative(), and TupleTableSlot::tts_tid.

◆ heapam_tuple_delete()

static TM_Result heapam_tuple_delete ( Relation  relation,
ItemPointer  tid,
CommandId  cid,
uint32  options,
Snapshot  snapshot,
Snapshot  crosscheck,
bool  wait,
TM_FailureData tmfd 
)
static

Definition at line 203 of file heapam_handler.c.

206{
207 /*
208 * Currently Deleting of index tuples are handled at vacuum, in case if
209 * the storage itself is cleaning the dead tuples by itself, it is the
210 * time to call the index tuple deletion also.
211 */
212 return heap_delete(relation, tid, cid, options, crosscheck, wait,
213 tmfd);
214}
TM_Result heap_delete(Relation relation, const ItemPointerData *tid, CommandId cid, uint32 options, Snapshot crosscheck, bool wait, TM_FailureData *tmfd)
Definition heapam.c:2717

References fb(), and heap_delete().

◆ heapam_tuple_insert()

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

Definition at line 150 of file heapam_handler.c.

152{
153 bool shouldFree = true;
154 HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
155
156 /* Update the tuple with table oid */
157 slot->tts_tableOid = RelationGetRelid(relation);
158 tuple->t_tableOid = slot->tts_tableOid;
159
160 /* Perform the insertion, and copy the resulting ItemPointer */
161 heap_insert(relation, tuple, cid, options, bistate);
162 ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
163
164 if (shouldFree)
165 pfree(tuple);
166}
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition itemptr.h:172
Oid t_tableOid
Definition htup.h:66

References ExecFetchSlotHeapTuple(), fb(), 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,
uint32  options,
BulkInsertState  bistate,
uint32  specToken 
)
static

Definition at line 169 of file heapam_handler.c.

172{
173 bool shouldFree = true;
174 HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
175
176 /* Update the tuple with table oid */
177 slot->tts_tableOid = RelationGetRelid(relation);
178 tuple->t_tableOid = slot->tts_tableOid;
179
182
183 /* Perform the insertion, and copy the resulting ItemPointer */
184 heap_insert(relation, tuple, cid, options, bistate);
185 ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
186
187 if (shouldFree)
188 pfree(tuple);
189}
#define HEAP_INSERT_SPECULATIVE
Definition heapam.h:39
static void HeapTupleHeaderSetSpeculativeToken(HeapTupleHeaderData *tup, BlockNumber token)

References ExecFetchSlotHeapTuple(), fb(), 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 265 of file heapam_handler.c.

269{
272 Buffer buffer;
273 HeapTuple tuple = &bslot->base.tupdata;
274 bool follow_updates;
275
277 tmfd->traversed = false;
278
280
282 tuple->t_self = *tid;
283 result = heap_lock_tuple(relation, tuple, cid, mode, wait_policy,
284 follow_updates, &buffer, tmfd);
285
286 if (result == TM_Updated &&
288 {
289 /* Should not encounter speculative tuple on recheck */
291
292 ReleaseBuffer(buffer);
293
294 if (!ItemPointerEquals(&tmfd->ctid, &tuple->t_self))
295 {
298
299 /* it was updated, so look at the updated version */
300 *tid = tmfd->ctid;
301 /* updated row should have xmin matching this xmax */
302 priorXmax = tmfd->xmax;
303
304 /* signal that a tuple later in the chain is getting locked */
305 tmfd->traversed = true;
306
307 /*
308 * fetch target tuple
309 *
310 * Loop here to deal with updated or busy tuples
311 */
313 for (;;)
314 {
318 errmsg("tuple to be locked was already moved to another partition due to concurrent update")));
319
320 tuple->t_self = *tid;
321 if (heap_fetch(relation, &SnapshotDirty, tuple, &buffer, true))
322 {
323 /*
324 * If xmin isn't what we're expecting, the slot must have
325 * been recycled and reused for an unrelated tuple. This
326 * implies that the latest version of the row was deleted,
327 * so we need do nothing. (Should be safe to examine xmin
328 * without getting buffer's content lock. We assume
329 * reading a TransactionId to be atomic, and Xmin never
330 * changes in an existing tuple, except to invalid or
331 * frozen, and neither of those can match priorXmax.)
332 */
334 priorXmax))
335 {
336 ReleaseBuffer(buffer);
337 return TM_Deleted;
338 }
339
340 /* otherwise xmin should not be dirty... */
344 errmsg_internal("t_xmin %u is uncommitted in tuple (%u,%u) to be updated in table \"%s\"",
345 SnapshotDirty.xmin,
348 RelationGetRelationName(relation))));
349
350 /*
351 * If tuple is being updated by other transaction then we
352 * have to wait for its commit/abort, or die trying.
353 */
355 {
356 ReleaseBuffer(buffer);
357 switch (wait_policy)
358 {
359 case LockWaitBlock:
361 relation, &tuple->t_self,
363 break;
364 case LockWaitSkip:
366 /* skip instead of waiting */
367 return TM_WouldBlock;
368 break;
369 case LockWaitError:
373 errmsg("could not obtain lock on row in relation \"%s\"",
374 RelationGetRelationName(relation))));
375 break;
376 }
377 continue; /* loop back to repeat heap_fetch */
378 }
379
380 /*
381 * If tuple was inserted by our own transaction, we have
382 * to check cmin against cid: cmin >= current CID means
383 * our command cannot see the tuple, so we should ignore
384 * it. Otherwise heap_lock_tuple() will throw an error,
385 * and so would any later attempt to update or delete the
386 * tuple. (We need not check cmax because
387 * HeapTupleSatisfiesDirty will consider a tuple deleted
388 * by our transaction dead, regardless of cmax.) We just
389 * checked that priorXmax == xmin, so we can test that
390 * variable instead of doing HeapTupleHeaderGetXmin again.
391 */
394 {
395 tmfd->xmax = priorXmax;
396
397 /*
398 * Cmin is the problematic value, so store that. See
399 * above.
400 */
401 tmfd->cmax = HeapTupleHeaderGetCmin(tuple->t_data);
402 ReleaseBuffer(buffer);
403 return TM_SelfModified;
404 }
405
406 /*
407 * This is a live tuple, so try to lock it again.
408 */
409 ReleaseBuffer(buffer);
410 goto tuple_lock_retry;
411 }
412
413 /*
414 * If the referenced slot was actually empty, the latest
415 * version of the row must have been deleted, so we need do
416 * nothing.
417 */
418 if (tuple->t_data == NULL)
419 {
420 Assert(!BufferIsValid(buffer));
421 return TM_Deleted;
422 }
423
424 /*
425 * As above, if xmin isn't what we're expecting, do nothing.
426 */
428 priorXmax))
429 {
430 ReleaseBuffer(buffer);
431 return TM_Deleted;
432 }
433
434 /*
435 * If we get here, the tuple was found but failed
436 * SnapshotDirty. Assuming the xmin is either a committed xact
437 * or our own xact (as it certainly should be if we're trying
438 * to modify the tuple), this must mean that the row was
439 * updated or deleted by either a committed xact or our own
440 * xact. If it was deleted, we can ignore it; if it was
441 * updated then chain up to the next version and repeat the
442 * whole process.
443 *
444 * As above, it should be safe to examine xmax and t_ctid
445 * without the buffer content lock, because they can't be
446 * changing. We'd better hold a buffer pin though.
447 */
448 if (ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
449 {
450 /* deleted, so forget about it */
451 ReleaseBuffer(buffer);
452 return TM_Deleted;
453 }
454
455 /* updated, so look at the updated row */
456 *tid = tuple->t_data->t_ctid;
457 /* updated row should have xmin matching this xmax */
459 ReleaseBuffer(buffer);
460 /* loop back to fetch next in chain */
461 }
462 }
463 else
464 {
465 /* tuple was deleted, so give up */
466 return TM_Deleted;
467 }
468 }
469
470 slot->tts_tableOid = RelationGetRelid(relation);
471 tuple->t_tableOid = slot->tts_tableOid;
472
473 /* store in slot, transferring existing pin */
474 ExecStorePinnedBufferHeapTuple(tuple, slot, buffer);
475
476 return result;
477}
uint32 result
CommandId HeapTupleHeaderGetCmin(const HeapTupleHeaderData *tup)
Definition combocid.c:104
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:4539
static bool HeapTupleHeaderIsSpeculative(const HeapTupleHeaderData *tup)
bool ItemPointerEquals(const ItemPointerData *pointer1, const ItemPointerData *pointer2)
Definition itemptr.c:35
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
Definition itemptr.h:197
bool ConditionalXactLockTableWait(TransactionId xid, bool logLockFailure)
Definition lmgr.c:739
@ XLTW_FetchUpdated
Definition lmgr.h:33
bool log_lock_failures
Definition lock.c:57
@ LockWaitSkip
Definition lockoptions.h:42
@ LockWaitBlock
Definition lockoptions.h:40
@ LockWaitError
Definition lockoptions.h:44
static char * errmsg
static PgChecksumMode mode
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition pgbench.c:77
#define InitDirtySnapshot(snapshotdata)
Definition snapmgr.h:42
ItemPointerData t_ctid
TransactionId xmax
Definition tableam.h:172
CommandId cmax
Definition tableam.h:173
ItemPointerData ctid
Definition tableam.h:171
TM_Result
Definition tableam.h:95
@ TM_Deleted
Definition tableam.h:115
@ TM_WouldBlock
Definition tableam.h:125
@ TM_Updated
Definition tableam.h:112
@ TM_SelfModified
Definition tableam.h:106
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition tableam.h:299
#define TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS
Definition tableam.h:297
#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(), fb(), heap_fetch(), heap_lock_tuple(), HeapTupleHeaderGetCmin(), HeapTupleHeaderGetUpdateXid(), HeapTupleHeaderGetXmin(), HeapTupleHeaderIsSpeculative(), InitDirtySnapshot, ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetOffsetNumber(), ItemPointerIndicatesMovedPartitions(), LockWaitBlock, LockWaitError, LockWaitSkip, log_lock_failures, mode, RelationGetRelationName, RelationGetRelid, ReleaseBuffer(), result, 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, and TM_FailureData::xmax.

◆ heapam_tuple_satisfies_snapshot()

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

Definition at line 122 of file heapam_handler.c.

124{
126 bool res;
127
129 Assert(BufferIsValid(bslot->buffer));
130
131 /*
132 * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
133 * Caller should be holding pin, but not lock.
134 */
136 res = HeapTupleSatisfiesVisibility(bslot->base.tuple, snapshot,
137 bslot->buffer);
139
140 return res;
141}

References Assert, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferIsValid(), fb(), HeapTupleSatisfiesVisibility(), LockBuffer(), and TTS_IS_BUFFERTUPLE.

◆ heapam_tuple_tid_valid()

static bool heapam_tuple_tid_valid ( TableScanDesc  scan,
ItemPointer  tid 
)
static

Definition at line 113 of file heapam_handler.c.

114{
116
117 return ItemPointerIsValid(tid) &&
119}
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
BlockNumber rs_nblocks
Definition heapam.h:63

References fb(), ItemPointerGetBlockNumber(), and ItemPointerIsValid().

◆ heapam_tuple_update()

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

Definition at line 218 of file heapam_handler.c.

223{
224 bool shouldFree = true;
225 HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);
227
228 /* Update the tuple with table oid */
229 slot->tts_tableOid = RelationGetRelid(relation);
230 tuple->t_tableOid = slot->tts_tableOid;
231
232 result = heap_update(relation, otid, tuple, cid, options,
233 crosscheck, wait,
234 tmfd, lockmode, update_indexes);
235 ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
236
237 /*
238 * Decide whether new index entries are needed for the tuple
239 *
240 * Note: heap_update returns the tid (location) of the new tuple in the
241 * t_self field.
242 *
243 * If the update is not HOT, we must update all indexes. If the update is
244 * HOT, it could be that we updated summarized columns, so we either
245 * update only summarized indexes, or none at all.
246 */
247 if (result != TM_Ok)
248 {
251 }
252 else if (!HeapTupleIsHeapOnly(tuple))
254 else
256 (*update_indexes == TU_None));
257
258 if (shouldFree)
259 pfree(tuple);
260
261 return result;
262}
TM_Result heap_update(Relation relation, const ItemPointerData *otid, HeapTuple newtup, CommandId cid, uint32 options pg_attribute_unused(), Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
Definition heapam.c:3201
@ TU_Summarizing
Definition tableam.h:141
@ TU_All
Definition tableam.h:138
@ TU_None
Definition tableam.h:135
@ TM_Ok
Definition tableam.h:100

References Assert, ExecFetchSlotHeapTuple(), fb(), heap_update(), HeapTupleIsHeapOnly(), ItemPointerCopy(), pfree(), RelationGetRelid, result, HeapTupleData::t_self, HeapTupleData::t_tableOid, TM_Ok, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_tid, TU_All, TU_None, and TU_Summarizing.

◆ 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 2346 of file heapam_handler.c.

2349{
2350 HeapTuple newtuple;
2351
2352 newtuple = reform_tuple(tuple, OldHeap, NewHeap, values, isnull);
2353
2354 /* The heap rewrite module does the rest */
2355 rewrite_heap_tuple(rwstate, tuple, newtuple);
2356
2357 heap_freetuple(newtuple);
2358}

References fb(), heap_freetuple(), reform_tuple(), rewrite_heap_tuple(), and values.

Referenced by heapam_relation_copy_for_cluster().

◆ reform_tuple()

static HeapTuple reform_tuple ( HeapTuple  tuple,
Relation  OldHeap,
Relation  NewHeap,
Datum values,
bool isnull 
)
static

Definition at line 2401 of file heapam_handler.c.

2403{
2406 bool needs_reform = false;
2407
2408 /*
2409 * A short tuple might require values from attmissing val, so activate the
2410 * coding unconditionally in that case. The value might legitimally be
2411 * NULL otherwise, so this is slightly wasteful, but it probably beats
2412 * having to test each attribute for presence of attmissingval each time.
2413 */
2414 if (HeapTupleHeaderGetNatts(tuple->t_data) < newTupDesc->natts)
2415 needs_reform = true;
2416
2417 /*
2418 * If the column has been dropped but a value is still present, we can
2419 * optimize storage now by getting rid of it.
2420 */
2421 if (!needs_reform)
2422 {
2423 for (int i = 0; i < newTupDesc->natts; i++)
2424 {
2425 if (TupleDescCompactAttr(newTupDesc, i)->attisdropped &&
2426 !heap_attisnull(tuple, i + 1, newTupDesc))
2427 {
2428 needs_reform = true;
2429 break;
2430 }
2431 }
2432 }
2433
2434 /* Skip work if no changes are needed */
2435 if (!needs_reform)
2436 return heap_copytuple(tuple);
2437
2438 heap_deform_tuple(tuple, oldTupDesc, values, isnull);
2439
2440 for (int i = 0; i < newTupDesc->natts; i++)
2441 {
2442 if (TupleDescCompactAttr(newTupDesc, i)->attisdropped)
2443 isnull[i] = true;
2444 }
2445
2446 return heap_form_tuple(newTupDesc, values, isnull);
2447}

References fb(), heap_attisnull(), heap_copytuple(), heap_deform_tuple(), heap_form_tuple(), HeapTupleHeaderGetNatts, i, RelationGetDescr, HeapTupleData::t_data, TupleDescCompactAttr(), and values.

Referenced by heap_insert_for_repack(), and reform_and_rewrite_tuple().

◆ SampleHeapTupleVisible()

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

Definition at line 2453 of file heapam_handler.c.

2456{
2458
2459 if (scan->rs_flags & SO_ALLOW_PAGEMODE)
2460 {
2461 uint32 start = 0,
2462 end = hscan->rs_ntuples;
2463
2464 /*
2465 * In pageatatime mode, heap_prepare_pagescan() already did visibility
2466 * checks, so just look at the info it left in rs_vistuples[].
2467 *
2468 * We use a binary search over the known-sorted array. Note: we could
2469 * save some effort if we insisted that NextSampleTuple select tuples
2470 * in increasing order, but it's not clear that there would be enough
2471 * gain to justify the restriction.
2472 */
2473 while (start < end)
2474 {
2475 uint32 mid = start + (end - start) / 2;
2476 OffsetNumber curoffset = hscan->rs_vistuples[mid];
2477
2478 if (tupoffset == curoffset)
2479 return true;
2480 else if (tupoffset < curoffset)
2481 end = mid;
2482 else
2483 start = mid + 1;
2484 }
2485
2486 return false;
2487 }
2488 else
2489 {
2490 /* Otherwise, we have to check the tuple individually. */
2491 return HeapTupleSatisfiesVisibility(tuple, scan->rs_snapshot,
2492 buffer);
2493 }
2494}

References fb(), HeapTupleSatisfiesVisibility(), TableScanDescData::rs_flags, TableScanDescData::rs_snapshot, SO_ALLOW_PAGEMODE, and start.

Referenced by heapam_scan_sample_next_tuple().

Variable Documentation

◆ heapam_methods

const TableAmRoutine heapam_methods
static

Definition at line 2659 of file heapam_handler.c.

2659 {
2660 .type = T_TableAmRoutine,
2661
2662 .slot_callbacks = heapam_slot_callbacks,
2663
2664 .scan_begin = heap_beginscan,
2665 .scan_end = heap_endscan,
2666 .scan_rescan = heap_rescan,
2667 .scan_getnextslot = heap_getnextslot,
2668
2669 .scan_set_tidrange = heap_set_tidrange,
2670 .scan_getnextslot_tidrange = heap_getnextslot_tidrange,
2671
2672 .parallelscan_estimate = table_block_parallelscan_estimate,
2673 .parallelscan_initialize = table_block_parallelscan_initialize,
2674 .parallelscan_reinitialize = table_block_parallelscan_reinitialize,
2675
2676 .index_fetch_begin = heapam_index_fetch_begin,
2677 .index_fetch_reset = heapam_index_fetch_reset,
2678 .index_fetch_end = heapam_index_fetch_end,
2679 .index_fetch_tuple = heapam_index_fetch_tuple,
2680
2681 .tuple_insert = heapam_tuple_insert,
2682 .tuple_insert_speculative = heapam_tuple_insert_speculative,
2683 .tuple_complete_speculative = heapam_tuple_complete_speculative,
2684 .multi_insert = heap_multi_insert,
2685 .tuple_delete = heapam_tuple_delete,
2686 .tuple_update = heapam_tuple_update,
2687 .tuple_lock = heapam_tuple_lock,
2688
2689 .tuple_fetch_row_version = heapam_fetch_row_version,
2690 .tuple_get_latest_tid = heap_get_latest_tid,
2691 .tuple_tid_valid = heapam_tuple_tid_valid,
2692 .tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,
2693 .index_delete_tuples = heap_index_delete_tuples,
2694
2695 .relation_set_new_filelocator = heapam_relation_set_new_filelocator,
2696 .relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,
2697 .relation_copy_data = heapam_relation_copy_data,
2698 .relation_copy_for_cluster = heapam_relation_copy_for_cluster,
2699 .relation_vacuum = heap_vacuum_rel,
2700 .scan_analyze_next_block = heapam_scan_analyze_next_block,
2701 .scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,
2702 .index_build_range_scan = heapam_index_build_range_scan,
2703 .index_validate_scan = heapam_index_validate_scan,
2704
2705 .relation_size = table_block_relation_size,
2706 .relation_needs_toast_table = heapam_relation_needs_toast_table,
2707 .relation_toast_am = heapam_relation_toast_am,
2708 .relation_fetch_toast_slice = heap_fetch_toast_slice,
2709
2710 .relation_estimate_size = heapam_estimate_rel_size,
2711
2712 .scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,
2713 .scan_sample_next_block = heapam_scan_sample_next_block,
2714 .scan_sample_next_tuple = heapam_scan_sample_next_tuple
2715};

Referenced by GetHeapamTableAmRoutine(), and heap_tableam_handler().