From 2142bcd253c9ab688f5a04b1012b858f4da4fdf8 Mon Sep 17 00:00:00 2001 From: Rui Zhao Date: Sat, 12 Sep 2026 13:44:05 +0800 Subject: [PATCH] Add tableam_index.h for the table AM side of index scans The inline index_getnext_tid added by the slot-based table AM index scan interface commit needs relscan.h, pgstat.h and utils/rel.h, and having amapi.h include those pulls pgstat.h into every translation unit that includes an index AM header (nbtree.h, hash.h, gist_private.h, spgist.h, brin_internal.h, gin_private.h) or reloptions.h. Only table AMs call index_getnext_tid, and the only in-tree caller is heapam_indexscan.c. Move it verbatim into a new header, access/tableam_index.h, that only table AMs need to include, and restore amapi.h's previous include list. The header also declares index_fill_ios_slot, the other helper meant only for table AMs, and its comment spells out what a table AM's xs_getnext_slot callback is expected to do, including maintaining ntabletuplefetches, which so far was only stated in the commit message. --- src/backend/access/heap/heapam_indexscan.c | 1 + src/backend/access/index/indexam.c | 1 + src/include/access/amapi.h | 36 --------- src/include/access/genam.h | 1 - src/include/access/tableam_index.h | 87 ++++++++++++++++++++++ 5 files changed, 89 insertions(+), 37 deletions(-) create mode 100644 src/include/access/tableam_index.h diff --git a/src/backend/access/heap/heapam_indexscan.c b/src/backend/access/heap/heapam_indexscan.c index a2d7a4152bf..3a263c5e738 100644 --- a/src/backend/access/heap/heapam_indexscan.c +++ b/src/backend/access/heap/heapam_indexscan.c @@ -17,6 +17,7 @@ #include "access/amapi.h" #include "access/heapam.h" #include "access/relscan.h" +#include "access/tableam_index.h" #include "access/visibilitymap.h" #include "storage/predicate.h" #include "utils/pgstat_internal.h" diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index befe9f60e26..4239b3df0a2 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -46,6 +46,7 @@ #include "access/reloptions.h" #include "access/relscan.h" #include "access/tableam.h" +#include "access/tableam_index.h" #include "catalog/index.h" #include "catalog/pg_type.h" #include "nodes/execnodes.h" diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h index 5ed9dcbbf68..79240333530 100644 --- a/src/include/access/amapi.h +++ b/src/include/access/amapi.h @@ -14,12 +14,9 @@ #include "access/cmptype.h" #include "access/genam.h" -#include "access/relscan.h" #include "access/stratnum.h" #include "nodes/nodes.h" #include "nodes/pg_list.h" -#include "pgstat.h" -#include "utils/rel.h" /* * We don't wish to include planner header files here, since most of an index @@ -335,37 +332,4 @@ extern const IndexAmRoutine *GetIndexAmRoutineByAmId(Oid amoid, bool noerror); extern CompareType IndexAmTranslateStrategy(StrategyNumber strategy, Oid amoid, Oid opfamily, bool missing_ok); extern StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok); -/* - * index_getnext_tid - amgettuple interface - * - * Fetch the next matching TID for the scan (or the first). Returns true when - * a TID was found (the index AM will have saved it in scan->xs_heaptid), or - * false when we're out of index entries. - */ -static inline bool -index_getnext_tid(IndexScanDesc scan, ScanDirection direction) -{ - bool found; - - Assert(RelationIsValid(scan->indexRelation)); - Assert(scan->indexRelation->rd_indam != NULL); - Assert(scan->indexRelation->rd_indam->amgettuple != NULL); - - found = scan->indexRelation->rd_indam->amgettuple(scan, direction); - - /* Reset kill flag immediately for safety */ - scan->kill_prior_tuple = false; - scan->xs_heap_continue = false; - - /* If we're out of index entries, we're done */ - if (!found) - return false; - - Assert(ItemPointerIsValid(&scan->xs_heaptid)); - - pgstat_count_index_tuples(scan->indexRelation, 1); - - return true; -} - #endif /* AMAPI_H */ diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 5e431682748..72b256ecf43 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -197,7 +197,6 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum); extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum); -extern void index_fill_ios_slot(IndexScanDesc scan, TupleTableSlot *slot); extern void index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, IndexOrderByDistance *distances, diff --git a/src/include/access/tableam_index.h b/src/include/access/tableam_index.h new file mode 100644 index 00000000000..10e19305713 --- /dev/null +++ b/src/include/access/tableam_index.h @@ -0,0 +1,87 @@ +/*------------------------------------------------------------------------- + * + * tableam_index.h + * Index scan support for table access methods. + * + * A table AM's index_scan_begin callback sets IndexScanDesc.xs_getnext_slot + * to the callback that table_index_getnext_slot() dispatches to. That + * callback is expected to: + * + * - Get TIDs from the index AM through index_getnext_tid() (below), which + * also resets the kill_prior_tuple and xs_heap_continue flags and counts + * the returned tuple for pg_stat_all_indexes.idx_tup_read. + * + * - During plain index scans, return each visible table tuple in the + * caller's slot (of the table AM's own slot type), and call + * pgstat_count_heap_fetch(scan->indexRelation) for it. With a non-MVCC + * snapshot more than one version reachable from a TID can be visible; the + * callback then returns each of them before getting the next TID (see + * xs_heap_continue in relscan.h). + * + * - During index-only scans, decide per TID whether a table fetch is needed + * to establish visibility, count each such fetch attempt in + * scan->instrument->ntabletuplefetches when scan->instrument is set + * (EXPLAIN ANALYZE reports it as "Heap Fetches"), call + * pgstat_count_heap_fetch() for each tuple found visible that way, and + * fill the caller's virtual slot from the index AM's xs_itup/xs_hitup + * through index_fill_ios_slot() (below). When scan->xs_visited_pages_limit + * is nonzero it is an approximate cap on the number of table pages to + * visit: return false once it has been exceeded without finding a visible + * tuple (see relscan.h). + * + * These helpers are only intended for use by table AMs. They are kept out + * of amapi.h so that the many index AM headers that include amapi.h don't + * have to pull in relscan.h, pgstat.h and rel.h. + * + * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/tableam_index.h + * + *------------------------------------------------------------------------- + */ +#ifndef TABLEAM_INDEX_H +#define TABLEAM_INDEX_H + +#include "access/amapi.h" +#include "access/relscan.h" +#include "pgstat.h" +#include "utils/rel.h" + +/* + * index_getnext_tid - amgettuple interface + * + * Fetch the next matching TID for the scan (or the first). Returns true when + * a TID was found (the index AM will have saved it in scan->xs_heaptid), or + * false when we're out of index entries. + */ +static inline bool +index_getnext_tid(IndexScanDesc scan, ScanDirection direction) +{ + bool found; + + Assert(RelationIsValid(scan->indexRelation)); + Assert(scan->indexRelation->rd_indam != NULL); + Assert(scan->indexRelation->rd_indam->amgettuple != NULL); + + found = scan->indexRelation->rd_indam->amgettuple(scan, direction); + + /* Reset kill flag immediately for safety */ + scan->kill_prior_tuple = false; + scan->xs_heap_continue = false; + + /* If we're out of index entries, we're done */ + if (!found) + return false; + + Assert(ItemPointerIsValid(&scan->xs_heaptid)); + + pgstat_count_index_tuples(scan->indexRelation, 1); + + return true; +} + +/* in access/index/indexam.c */ +extern void index_fill_ios_slot(IndexScanDesc scan, TupleTableSlot *slot); + +#endif /* TABLEAM_INDEX_H */ -- 2.43.7