static const TableAmRoutine heapam_methods = {
.type = T_TableAmRoutine,
.slot_callbacks = heapam_slot_callbacks,
.scan_begin = heap_beginscan,
.scan_end = heap_endscan,
.scan_rescan = heap_rescan,
.scan_getnextslot = heap_getnextslot,
.parallelscan_estimate = table_block_parallelscan_estimate,
.parallelscan_initialize = table_block_parallelscan_initialize,
.parallelscan_reinitialize = table_block_parallelscan_reinitialize,
...
}
heap_beginscan
heap_beginscan函数用于初始化HeapScanDescData结构体,ParallelTableScanDesc形参在ExecGather函数中ExecSeqScanInitializeDSM函数会调用TableAM提供的table_beginscan_parallel函数,该函数中会申请ParallelTableScanDesc内存,而串行scan时,heap_beginscan函数在SeqNext函数执行时调用,但是ParallelTableScanDesc形参为null。
TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key,
ParallelTableScanDesc parallel_scan, uint32 flags) {
RelationIncrementReferenceCount(relation);
HeapScanDesc scan = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
scan->rs_base.rs_rd = relation; scan->rs_base.rs_snapshot = snapshot; scan->rs_base.rs_nkeys = nkeys;
scan->rs_base.rs_flags = flags; scan->rs_base.rs_parallel = parallel_scan; scan->rs_strategy = NULL; /* set in initscan */
if (!(snapshot && IsMVCCSnapshot(snapshot))) scan->rs_base.rs_flags &= ~SO_ALLOW_PAGEMODE; /* Disable page-at-a-time mode if it's not a MVCC-safe snapshot. */
if (scan->rs_base.rs_flags & (SO_TYPE_SEQSCAN | SO_TYPE_SAMPLESCAN)){
/* Ensure a missing snapshot is noticed reliably, even if the isolation mode means predicate locking isn't performed (and therefore the snapshot isn't used here). */
PredicateLockRelation(relation, snapshot);
}
scan->rs_ctup.t_tableOid = RelationGetRelid(relation); /* we only need to set this up once */
/* we do this here instead of in initscan() because heap_rescan also calls initscan() and we don't want to allocate memory again */
if (nkeys > 0) scan->rs_base.rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
else scan->rs_base.rs_key = NULL;
initscan(scan, key, false);
return (TableScanDesc) scan;
}
heap_endscan
void heap_endscan(TableScanDesc sscan) {
HeapScanDesc scan = (HeapScanDesc) sscan;
/* unpin scan buffers */
if (BufferIsValid(scan->rs_cbuf)) ReleaseBuffer(scan->rs_cbuf);
RelationDecrementReferenceCount(scan->rs_base.rs_rd);
if (scan->rs_base.rs_key) pfree(scan->rs_base.rs_key);
if (scan->rs_strategy != NULL) FreeAccessStrategy(scan->rs_strategy);
if (scan->rs_base.rs_flags & SO_TEMP_SNAPSHOT)
UnregisterSnapshot(scan->rs_base.rs_snapshot);
pfree(scan);
}
heap_getnextslot
heap_getnext函数获取tuple数据元组,分为两种页扫描模式:如果scan->rs_base.rs_flags为SO_ALLOW_PAGEMODE即扫描所有满足可见性的记录heapgettup_pagemode,否则扫描所有记录heapgettup。
typedef enum ScanDirection {
BackwardScanDirection = -1,
NoMovementScanDirection = 0, // re-fetch the tuple indicated by scan->rs_ctup
ForwardScanDirection = 1
} ScanDirection;
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction) {
HeapScanDesc scan = (HeapScanDesc) sscan;
if (unlikely(sscan->rs_rd->rd_tableam != GetHeapamTableAmRoutine()))
ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg_internal("only heap AM is supported")));
if (scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
heapgettup_pagemode(scan, direction, scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
else
heapgettup(scan, direction, scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
if (scan->rs_ctup.t_data == NULL) { return NULL } // returning EOS
pgstat_count_heap_getnext(scan->rs_base.rs_rd);
return &scan->rs_ctup;
}