{"Entry":{"collection":"tableam","key":"heap","name":"heap","aliases":[],"metadata":{"aliases":[],"category":"Core table AM","content_hash":"1612ad33787d9f68db16cce2bce26afa690606249036105dea14c90c64b68b36","imported_at":"2026-09-30T00:40:47.142241+08:00","name":"heap","name_zh":"Core table AM","slug":"heap","summary":"The built-in heap table access method integrates tuple storage, visibility, scans, index fetches and maintenance with PostgreSQL."}},"Definition":{"Collection":"tableam","Key":"heap","SourceDatabase":"center","Version":"18","SourceTable":"table_am","SourceKey":"heap","SourceRevision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","Facts":{"callbacks":{"index_build_range_scan":"heapam_index_build_range_scan","index_delete_tuples":"heap_index_delete_tuples","index_fetch_begin":"heapam_index_fetch_begin","index_fetch_end":"heapam_index_fetch_end","index_fetch_reset":"heapam_index_fetch_reset","index_fetch_tuple":"heapam_index_fetch_tuple","index_validate_scan":"heapam_index_validate_scan","multi_insert":"heap_multi_insert","parallelscan_estimate":"table_block_parallelscan_estimate","parallelscan_initialize":"table_block_parallelscan_initialize","parallelscan_reinitialize":"table_block_parallelscan_reinitialize","relation_copy_data":"heapam_relation_copy_data","relation_copy_for_cluster":"heapam_relation_copy_for_cluster","relation_estimate_size":"heapam_estimate_rel_size","relation_fetch_toast_slice":"heap_fetch_toast_slice","relation_needs_toast_table":"heapam_relation_needs_toast_table","relation_nontransactional_truncate":"heapam_relation_nontransactional_truncate","relation_set_new_filelocator":"heapam_relation_set_new_filelocator","relation_size":"table_block_relation_size","relation_toast_am":"heapam_relation_toast_am","relation_vacuum":"heap_vacuum_rel","scan_analyze_next_block":"heapam_scan_analyze_next_block","scan_analyze_next_tuple":"heapam_scan_analyze_next_tuple","scan_begin":"heap_beginscan","scan_bitmap_next_tuple":"heapam_scan_bitmap_next_tuple","scan_end":"heap_endscan","scan_getnextslot":"heap_getnextslot","scan_getnextslot_tidrange":"heap_getnextslot_tidrange","scan_rescan":"heap_rescan","scan_sample_next_block":"heapam_scan_sample_next_block","scan_sample_next_tuple":"heapam_scan_sample_next_tuple","scan_set_tidrange":"heap_set_tidrange","slot_callbacks":"heapam_slot_callbacks","tuple_complete_speculative":"heapam_tuple_complete_speculative","tuple_delete":"heapam_tuple_delete","tuple_fetch_row_version":"heapam_fetch_row_version","tuple_get_latest_tid":"heap_get_latest_tid","tuple_insert":"heapam_tuple_insert","tuple_insert_speculative":"heapam_tuple_insert_speculative","tuple_lock":"heapam_tuple_lock","tuple_satisfies_snapshot":"heapam_tuple_satisfies_snapshot","tuple_tid_valid":"heapam_tuple_tid_valid","tuple_update":"heapam_tuple_update"},"comparison_data":{"callbacks":{"index_build_range_scan":"heapam_index_build_range_scan","index_delete_tuples":"heap_index_delete_tuples","index_fetch_begin":"heapam_index_fetch_begin","index_fetch_end":"heapam_index_fetch_end","index_fetch_reset":"heapam_index_fetch_reset","index_fetch_tuple":"heapam_index_fetch_tuple","index_validate_scan":"heapam_index_validate_scan","multi_insert":"heap_multi_insert","parallelscan_estimate":"table_block_parallelscan_estimate","parallelscan_initialize":"table_block_parallelscan_initialize","parallelscan_reinitialize":"table_block_parallelscan_reinitialize","relation_copy_data":"heapam_relation_copy_data","relation_copy_for_cluster":"heapam_relation_copy_for_cluster","relation_estimate_size":"heapam_estimate_rel_size","relation_fetch_toast_slice":"heap_fetch_toast_slice","relation_needs_toast_table":"heapam_relation_needs_toast_table","relation_nontransactional_truncate":"heapam_relation_nontransactional_truncate","relation_set_new_filelocator":"heapam_relation_set_new_filelocator","relation_size":"table_block_relation_size","relation_toast_am":"heapam_relation_toast_am","relation_vacuum":"heap_vacuum_rel","scan_analyze_next_block":"heapam_scan_analyze_next_block","scan_analyze_next_tuple":"heapam_scan_analyze_next_tuple","scan_begin":"heap_beginscan","scan_bitmap_next_tuple":"heapam_scan_bitmap_next_tuple","scan_end":"heap_endscan","scan_getnextslot":"heap_getnextslot","scan_getnextslot_tidrange":"heap_getnextslot_tidrange","scan_rescan":"heap_rescan","scan_sample_next_block":"heapam_scan_sample_next_block","scan_sample_next_tuple":"heapam_scan_sample_next_tuple","scan_set_tidrange":"heap_set_tidrange","slot_callbacks":"heapam_slot_callbacks","tuple_complete_speculative":"heapam_tuple_complete_speculative","tuple_delete":"heapam_tuple_delete","tuple_fetch_row_version":"heapam_fetch_row_version","tuple_get_latest_tid":"heap_get_latest_tid","tuple_insert":"heapam_tuple_insert","tuple_insert_speculative":"heapam_tuple_insert_speculative","tuple_lock":"heapam_tuple_lock","tuple_satisfies_snapshot":"heapam_tuple_satisfies_snapshot","tuple_tid_valid":"heapam_tuple_tid_valid","tuple_update":"heapam_tuple_update"},"options":[],"protocol_versions":{},"source_options":[]},"comparison_hash":"1238506f6e6c6648382f3362465d376e28e55ca6108f975b44f413d320769112","description":["The built-in heap table access method integrates tuple storage, visibility, scans, index fetches and maintenance with PostgreSQL."],"evidence_kind":"source and documentation","facts":[{"label":"Interface family","value":"Table access method"},{"label":"Handler or routine","value":"heapam_methods"},{"label":"Recorded callbacks","value":"43"}],"manual_html":"\u003cdiv class=\"chapter\" id=\"TABLEAM\"\u003e\n\u003cdiv class=\"titlepage\"\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch2 class=\"title\"\u003eChapter 62. Table Access Method Interface Definition\u003c/h2\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cp\u003eThis chapter explains the interface between the core \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e system and \u003cem class=\"firstterm\"\u003etable access methods\u003c/em\u003e, which manage the storage for tables. The core system knows little about these access methods beyond what is specified here, so it is possible to develop entirely new access method types by writing add-on code.\u003c/p\u003e\n\u003cp\u003eEach table access method is described by a row in the \u003ca class=\"link\" href=\"/docs/18/catalog-pg-am.html\" title=\"52.3. pg_am\"\u003e\u003ccode class=\"structname\"\u003epg_am\u003c/code\u003e\u003c/a\u003e system catalog. The \u003ccode class=\"structname\"\u003epg_am\u003c/code\u003e entry specifies a name and a \u003cem class=\"firstterm\"\u003ehandler function\u003c/em\u003e for the table access method. These entries can be created and deleted using the \u003ca class=\"xref\" href=\"/docs/18/sql-create-access-method.html\" title=\"CREATE ACCESS METHOD\"\u003e\u003cspan class=\"refentrytitle\"\u003eCREATE ACCESS METHOD\u003c/span\u003e\u003c/a\u003e and \u003ca class=\"xref\" href=\"/docs/18/sql-drop-access-method.html\" title=\"DROP ACCESS METHOD\"\u003e\u003cspan class=\"refentrytitle\"\u003eDROP ACCESS METHOD\u003c/span\u003e\u003c/a\u003e SQL commands.\u003c/p\u003e\n\u003cp\u003eA table access method handler function must be declared to accept a single argument of type \u003ccode class=\"type\"\u003einternal\u003c/code\u003e and to return the pseudo-type \u003ccode class=\"type\"\u003etable_am_handler\u003c/code\u003e. The argument is a dummy value that simply serves to prevent handler functions from being called directly from SQL commands.\u003c/p\u003e\n\u003cp\u003eHere is how an extension SQL script file might create a table access method handler:\u003c/p\u003e\n\u003cpre class=\"programlisting\"\u003eCREATE OR REPLACE FUNCTION my_tableam_handler(internal)\n  RETURNS table_am_handler AS 'my_extension', 'my_tableam_handler'\n  LANGUAGE C STRICT;\n\nCREATE ACCESS METHOD myam TYPE TABLE HANDLER my_tableam_handler;\n\u003c/pre\u003e\n\u003cp\u003eThe result of the function must be a pointer to a struct of type \u003ccode class=\"structname\"\u003eTableAmRoutine\u003c/code\u003e, which contains everything that the core code needs to know to make use of the table access method. The return value needs to be of server lifetime, which is typically achieved by defining it as a \u003ccode class=\"literal\"\u003estatic const\u003c/code\u003e variable in global scope.\u003c/p\u003e\n\u003cp\u003eHere is how a source file with the table access method handler might look like:\u003c/p\u003e\n\u003cpre class=\"programlisting\"\u003e#include \"postgres.h\"\n\n#include \"access/tableam.h\"\n#include \"fmgr.h\"\n\nPG_MODULE_MAGIC;\n\nstatic const TableAmRoutine my_tableam_methods = {\n    .type = T_TableAmRoutine,\n\n    /* Methods of TableAmRoutine omitted from example, add them here. */\n};\n\nPG_FUNCTION_INFO_V1(my_tableam_handler);\n\nDatum\nmy_tableam_handler(PG_FUNCTION_ARGS)\n{\n    PG_RETURN_POINTER(\u0026amp;my_tableam_methods);\n}\n\n\u003c/pre\u003e\n\u003cp\u003eThe \u003ccode class=\"structname\"\u003eTableAmRoutine\u003c/code\u003e struct, also called the access method's \u003cem class=\"firstterm\"\u003eAPI struct\u003c/em\u003e, defines the behavior of the access method using callbacks. These callbacks are pointers to plain C functions and are not visible or callable at the SQL level. All the callbacks and their behavior is defined in the \u003ccode class=\"structname\"\u003eTableAmRoutine\u003c/code\u003e structure (with comments inside the struct defining the requirements for callbacks). Most callbacks have wrapper functions, which are documented from the point of view of a user (rather than an implementor) of the table access method. For details, please refer to the \u003ca class=\"ulink\" href=\"https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/access/tableam.h;hb=HEAD\"\u003e\u003ccode class=\"filename\"\u003esrc/include/access/tableam.h\u003c/code\u003e\u003c/a\u003e file.\u003c/p\u003e\n\u003cp\u003eTo implement an access method, an implementor will typically need to implement an AM-specific type of tuple table slot (see \u003ca class=\"ulink\" href=\"https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/executor/tuptable.h;hb=HEAD\"\u003e\u003ccode class=\"filename\"\u003esrc/include/executor/tuptable.h\u003c/code\u003e\u003c/a\u003e), which allows code outside the access method to hold references to tuples of the AM, and to access the columns of the tuple.\u003c/p\u003e\n\u003cp\u003eCurrently, the way an AM actually stores data is fairly unconstrained. For example, it's possible, but not required, to use postgres' shared buffer cache. In case it is used, it likely makes sense to use \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e's standard page layout as described in \u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html\" title=\"66.6. Database Page Layout\"\u003eSection 66.6\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eOne fairly large constraint of the table access method API is that, currently, if the AM wants to support modifications and/or indexes, it is necessary for each tuple to have a tuple identifier (TID) consisting of a block number and an item number (see also \u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html\" title=\"66.6. Database Page Layout\"\u003eSection 66.6\u003c/a\u003e). It is not strictly necessary that the sub-parts of TIDs have the same meaning they e.g., have for \u003ccode class=\"literal\"\u003eheap\u003c/code\u003e, but if bitmap scan support is desired (it is optional), the block number needs to provide locality.\u003c/p\u003e\n\u003cp\u003eFor crash safety, an AM can use postgres' \u003ca class=\"link\" href=\"/docs/18/wal.html\" title=\"Chapter 28. Reliability and the Write-Ahead Log\"\u003eWAL\u003c/a\u003e, or a custom implementation. If WAL is chosen, either \u003ca class=\"link\" href=\"/docs/18/generic-wal.html\" title=\"64.1. Generic WAL Records\"\u003eGeneric WAL Records\u003c/a\u003e can be used, or a \u003ca class=\"link\" href=\"/docs/18/custom-rmgr.html\" title=\"64.2. Custom WAL Resource Managers\"\u003eCustom WAL Resource Manager\u003c/a\u003e can be implemented.\u003c/p\u003e\n\u003cp\u003eTo implement transactional support in a manner that allows different table access methods be accessed within a single transaction, it likely is necessary to closely integrate with the machinery in \u003ccode class=\"filename\"\u003esrc/backend/access/transam/xlog.c\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAny developer of a new \u003ccode class=\"literal\"\u003etable access method\u003c/code\u003e can refer to the existing \u003ccode class=\"literal\"\u003eheap\u003c/code\u003e implementation present in \u003ccode class=\"filename\"\u003esrc/backend/access/heap/heapam_handler.c\u003c/code\u003e for details of its implementation.\u003c/p\u003e\n\u003c/div\u003e","manual_path":"/docs/18/tableam.html","related":[{"label":"System catalog pg_am","url":"/wiki/catalog/pg_am/?v=18"},{"label":"default_table_access_method","url":"/wiki/guc/default_table_access_method/?v=18"},{"label":"Index AM","url":"/wiki/indexam/?v=18"}],"release":{"channel":"stable","label":"18.6","major":"18","ref":"PostgreSQL 18.6 source archive","revision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","source_snapshot_utc":"","source_url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},"runtime_verified":false,"sections":[{"paragraphs":["The matrix records callbacks actually registered in this source build. Registration identifies an implemented interface hook; options, query shape, privileges and provider rules determine whether an operation is allowed.","The complete same-version manual below retains configuration, constraints and examples. No runtime capability test is claimed."],"title":"Interface and capability boundaries"},{"paragraphs":["This inventory starts with the Table AM interface in PostgreSQL 12. Earlier PostgreSQL heap storage is not relabeled as a Table AM implementation.","Heap visibility follows PostgreSQL snapshots. WAL and crash-recovery behavior depend on relation persistence; temporary and unlogged tables differ from permanent tables. Index access methods remain separate from the table storage interface."],"title":"Heap storage, visibility and WAL"},{"code":"{\n\t.type = T_TableAmRoutine,\n\n\t.slot_callbacks = heapam_slot_callbacks,\n\n\t.scan_begin = heap_beginscan,\n\t.scan_end = heap_endscan,\n\t.scan_rescan = heap_rescan,\n\t.scan_getnextslot = heap_getnextslot,\n\n\t.scan_set_tidrange = heap_set_tidrange,\n\t.scan_getnextslot_tidrange = heap_getnextslot_tidrange,\n\n\t.parallelscan_estimate = table_block_parallelscan_estimate,\n\t.parallelscan_initialize = table_block_parallelscan_initialize,\n\t.parallelscan_reinitialize = table_block_parallelscan_reinitialize,\n\n\t.index_fetch_begin = heapam_index_fetch_begin,\n\t.index_fetch_reset = heapam_index_fetch_reset,\n\t.index_fetch_end = heapam_index_fetch_end,\n\t.index_fetch_tuple = heapam_index_fetch_tuple,\n\n\t.tuple_insert = heapam_tuple_insert,\n\t.tuple_insert_speculative = heapam_tuple_insert_speculative,\n\t.tuple_complete_speculative = heapam_tuple_complete_speculative,\n\t.multi_insert = heap_multi_insert,\n\t.tuple_delete = heapam_tuple_delete,\n\t.tuple_update = heapam_tuple_update,\n\t.tuple_lock = heapam_tuple_lock,\n\n\t.tuple_fetch_row_version = heapam_fetch_row_version,\n\t.tuple_get_latest_tid = heap_get_latest_tid,\n\t.tuple_tid_valid = heapam_tuple_tid_valid,\n\t.tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,\n\t.index_delete_tuples = heap_index_delete_tuples,\n\n\t.relation_set_new_filelocator = heapam_relation_set_new_filelocator,\n\t.relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,\n\t.relation_copy_data = heapam_relation_copy_data,\n\t.relation_copy_for_cluster = heapam_relation_copy_for_cluster,\n\t.relation_vacuum = heap_vacuum_rel,\n\t.scan_analyze_next_block = heapam_scan_analyze_next_block,\n\t.scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,\n\t.index_build_range_scan = heapam_index_build_range_scan,\n\t.index_validate_scan = heapam_index_validate_scan,\n\n\t.relation_size = table_block_relation_size,\n\t.relation_needs_toast_table = heapam_relation_needs_toast_table,\n\t.relation_toast_am = heapam_relation_toast_am,\n\t.relation_fetch_toast_slice = heap_fetch_toast_slice,\n\n\t.relation_estimate_size = heapam_estimate_rel_size,\n\n\t.scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,\n\t.scan_sample_next_block = heapam_scan_sample_next_block,\n\t.scan_sample_next_tuple = heapam_scan_sample_next_tuple\n}","title":"Registered implementation in core source"}],"sources":[{"label":"PostgreSQL 18 English manual","path":"tableam.html","sha256":"e659dece936c960dfea9e801720deb3d76abf2c3696abb3f58d32ac17d0f22aa","url":"/docs/18/tableam.html"},{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/access/heap/heapam_handler.c:2616","line":2616,"path":"src/backend/access/heap/heapam_handler.c","sha256":"fd60a1f8d5530ce5c33fda6b74123c3c22e8c3b9ca6455a0e5d47aa392797a1e","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"label":"PostgreSQL 18 English manual","path":"mvcc-intro.html","sha256":"456469051ccd15ec2cfa1f2e656901a3bb63414db367003215ff810d638593e1","url":"/docs/18/mvcc-intro.html"},{"label":"PostgreSQL 18 English manual","path":"sql-createtable.html","sha256":"b4d703391fe1102d0e02bd6769bef928513eb7a5542f38f7448fc77fe69e0d5b","url":"/docs/18/sql-createtable.html"},{"label":"PostgreSQL 18 English manual","path":"routine-vacuuming.html","sha256":"ede8964df385f5bc69ae991369e6eaf99ae58aa2d6210e8bf868f090bb95b002","url":"/docs/18/routine-vacuuming.html"},{"label":"PostgreSQL 18 English manual","path":"catalog-pg-am.html","sha256":"579364179365a51945e2edb6481df5d198916c2c725e506f166824dedf08994c","url":"/docs/18/catalog-pg-am.html"}],"tables":[{"columns":[{"key":"feature","label":"Interface operation"},{"key":"state","label":"Source observation"},{"key":"callback","label":"Callback"},{"key":"implementation","label":"Implementation"}],"key":"callbacks","rows":[{"callback":"slot_callbacks","feature":"Tuple slot","implementation":"heapam_slot_callbacks","state":"Handler registered; conditions apply"},{"callback":"scan_begin","feature":"Begin scan","implementation":"heap_beginscan","state":"Handler registered; conditions apply"},{"callback":"parallelscan_initialize","feature":"Parallel scan initialization","implementation":"table_block_parallelscan_initialize","state":"Handler registered; conditions apply"},{"callback":"index_fetch_begin","feature":"Begin index lookup","implementation":"heapam_index_fetch_begin","state":"Handler registered; conditions apply"},{"callback":"index_fetch_tuple","feature":"Fetch identified tuple","implementation":"heapam_index_fetch_tuple","state":"Handler registered; conditions apply"},{"callback":"tuple_insert","feature":"Insert tuple","implementation":"heapam_tuple_insert","state":"Handler registered; conditions apply"},{"callback":"tuple_update","feature":"Update tuple","implementation":"heapam_tuple_update","state":"Handler registered; conditions apply"},{"callback":"tuple_delete","feature":"Delete tuple","implementation":"heapam_tuple_delete","state":"Handler registered; conditions apply"},{"callback":"relation_vacuum","feature":"Vacuum relation","implementation":"heap_vacuum_rel","state":"Handler registered; conditions apply"},{"callback":"scan_analyze_next_block","feature":"Analyze block","implementation":"heapam_scan_analyze_next_block","state":"Handler registered; conditions apply"},{"callback":"relation_needs_toast_table","feature":"TOAST decision","implementation":"heapam_relation_needs_toast_table","state":"Handler registered; conditions apply"}],"title":"Registered interface handlers"}]},"ManualEvidence":{"manual_path":"/docs/18/tableam.html","release":{"channel":"stable","label":"18.6","major":"18","ref":"PostgreSQL 18.6 source archive","revision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","source_snapshot_utc":"","source_url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},"sources":[{"label":"PostgreSQL 18 English manual","path":"tableam.html","sha256":"e659dece936c960dfea9e801720deb3d76abf2c3696abb3f58d32ac17d0f22aa","url":"/docs/18/tableam.html"},{"archive_sha256":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","label":"src/backend/access/heap/heapam_handler.c:2616","line":2616,"path":"src/backend/access/heap/heapam_handler.c","sha256":"fd60a1f8d5530ce5c33fda6b74123c3c22e8c3b9ca6455a0e5d47aa392797a1e","url":"https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2"},{"label":"PostgreSQL 18 English manual","path":"mvcc-intro.html","sha256":"456469051ccd15ec2cfa1f2e656901a3bb63414db367003215ff810d638593e1","url":"/docs/18/mvcc-intro.html"},{"label":"PostgreSQL 18 English manual","path":"sql-createtable.html","sha256":"b4d703391fe1102d0e02bd6769bef928513eb7a5542f38f7448fc77fe69e0d5b","url":"/docs/18/sql-createtable.html"},{"label":"PostgreSQL 18 English manual","path":"routine-vacuuming.html","sha256":"ede8964df385f5bc69ae991369e6eaf99ae58aa2d6210e8bf868f090bb95b002","url":"/docs/18/routine-vacuuming.html"},{"label":"PostgreSQL 18 English manual","path":"catalog-pg-am.html","sha256":"579364179365a51945e2edb6481df5d198916c2c725e506f166824dedf08994c","url":"/docs/18/catalog-pg-am.html"}]},"MeasuredEvidence":{"runtime_verified":false}},"Text":{"Collection":"tableam","Key":"heap","SourceDatabase":"center","Version":"18","Locale":"en","Title":"heap","Summary":"The built-in heap table access method integrates tuple storage, visibility, scans, index fetches and maintenance with PostgreSQL.","BodyHTML":"\u003cdiv id=\"TABLEAM\"\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch2\u003eChapter 62. Table Access Method Interface Definition\u003c/h2\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cp\u003eThis chapter explains the interface between the core \u003cspan\u003ePostgreSQL\u003c/span\u003e system and \u003cem\u003etable access methods\u003c/em\u003e, which manage the storage for tables. The core system knows little about these access methods beyond what is specified here, so it is possible to develop entirely new access method types by writing add-on code.\u003c/p\u003e\n\u003cp\u003eEach table access method is described by a row in the \u003ca href=\"/docs/18/catalog-pg-am.html\" rel=\"nofollow\"\u003e\u003ccode\u003epg_am\u003c/code\u003e\u003c/a\u003e system catalog. The \u003ccode\u003epg_am\u003c/code\u003e entry specifies a name and a \u003cem\u003ehandler function\u003c/em\u003e for the table access method. These entries can be created and deleted using the \u003ca href=\"/docs/18/sql-create-access-method.html\" title=\"CREATE ACCESS METHOD\" rel=\"nofollow\"\u003e\u003cspan\u003eCREATE ACCESS METHOD\u003c/span\u003e\u003c/a\u003e and \u003ca href=\"/docs/18/sql-drop-access-method.html\" title=\"DROP ACCESS METHOD\" rel=\"nofollow\"\u003e\u003cspan\u003eDROP ACCESS METHOD\u003c/span\u003e\u003c/a\u003e SQL commands.\u003c/p\u003e\n\u003cp\u003eA table access method handler function must be declared to accept a single argument of type \u003ccode\u003einternal\u003c/code\u003e and to return the pseudo-type \u003ccode\u003etable_am_handler\u003c/code\u003e. The argument is a dummy value that simply serves to prevent handler functions from being called directly from SQL commands.\u003c/p\u003e\n\u003cp\u003eHere is how an extension SQL script file might create a table access method handler:\u003c/p\u003e\n\u003cpre\u003eCREATE OR REPLACE FUNCTION my_tableam_handler(internal)\n  RETURNS table_am_handler AS \u0026#39;my_extension\u0026#39;, \u0026#39;my_tableam_handler\u0026#39;\n  LANGUAGE C STRICT;\n\nCREATE ACCESS METHOD myam TYPE TABLE HANDLER my_tableam_handler;\n\u003c/pre\u003e\n\u003cp\u003eThe result of the function must be a pointer to a struct of type \u003ccode\u003eTableAmRoutine\u003c/code\u003e, which contains everything that the core code needs to know to make use of the table access method. The return value needs to be of server lifetime, which is typically achieved by defining it as a \u003ccode\u003estatic const\u003c/code\u003e variable in global scope.\u003c/p\u003e\n\u003cp\u003eHere is how a source file with the table access method handler might look like:\u003c/p\u003e\n\u003cpre\u003e#include \u0026#34;postgres.h\u0026#34;\n\n#include \u0026#34;access/tableam.h\u0026#34;\n#include \u0026#34;fmgr.h\u0026#34;\n\nPG_MODULE_MAGIC;\n\nstatic const TableAmRoutine my_tableam_methods = {\n    .type = T_TableAmRoutine,\n\n    /* Methods of TableAmRoutine omitted from example, add them here. */\n};\n\nPG_FUNCTION_INFO_V1(my_tableam_handler);\n\nDatum\nmy_tableam_handler(PG_FUNCTION_ARGS)\n{\n    PG_RETURN_POINTER(\u0026amp;my_tableam_methods);\n}\n\n\u003c/pre\u003e\n\u003cp\u003eThe \u003ccode\u003eTableAmRoutine\u003c/code\u003e struct, also called the access method\u0026#39;s \u003cem\u003eAPI struct\u003c/em\u003e, defines the behavior of the access method using callbacks. These callbacks are pointers to plain C functions and are not visible or callable at the SQL level. All the callbacks and their behavior is defined in the \u003ccode\u003eTableAmRoutine\u003c/code\u003e structure (with comments inside the struct defining the requirements for callbacks). Most callbacks have wrapper functions, which are documented from the point of view of a user (rather than an implementor) of the table access method. For details, please refer to the \u003ca href=\"https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/access/tableam.h;hb=HEAD\" rel=\"nofollow\"\u003e\u003ccode\u003esrc/include/access/tableam.h\u003c/code\u003e\u003c/a\u003e file.\u003c/p\u003e\n\u003cp\u003eTo implement an access method, an implementor will typically need to implement an AM-specific type of tuple table slot (see \u003ca href=\"https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/executor/tuptable.h;hb=HEAD\" rel=\"nofollow\"\u003e\u003ccode\u003esrc/include/executor/tuptable.h\u003c/code\u003e\u003c/a\u003e), which allows code outside the access method to hold references to tuples of the AM, and to access the columns of the tuple.\u003c/p\u003e\n\u003cp\u003eCurrently, the way an AM actually stores data is fairly unconstrained. For example, it\u0026#39;s possible, but not required, to use postgres\u0026#39; shared buffer cache. In case it is used, it likely makes sense to use \u003cspan\u003ePostgreSQL\u003c/span\u003e\u0026#39;s standard page layout as described in \u003ca href=\"/docs/18/storage-page-layout.html\" rel=\"nofollow\"\u003eSection 66.6\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eOne fairly large constraint of the table access method API is that, currently, if the AM wants to support modifications and/or indexes, it is necessary for each tuple to have a tuple identifier (TID) consisting of a block number and an item number (see also \u003ca href=\"/docs/18/storage-page-layout.html\" rel=\"nofollow\"\u003eSection 66.6\u003c/a\u003e). It is not strictly necessary that the sub-parts of TIDs have the same meaning they e.g., have for \u003ccode\u003eheap\u003c/code\u003e, but if bitmap scan support is desired (it is optional), the block number needs to provide locality.\u003c/p\u003e\n\u003cp\u003eFor crash safety, an AM can use postgres\u0026#39; \u003ca href=\"/docs/18/wal.html\" rel=\"nofollow\"\u003eWAL\u003c/a\u003e, or a custom implementation. If WAL is chosen, either \u003ca href=\"/docs/18/generic-wal.html\" rel=\"nofollow\"\u003eGeneric WAL Records\u003c/a\u003e can be used, or a \u003ca href=\"/docs/18/custom-rmgr.html\" rel=\"nofollow\"\u003eCustom WAL Resource Manager\u003c/a\u003e can be implemented.\u003c/p\u003e\n\u003cp\u003eTo implement transactional support in a manner that allows different table access methods be accessed within a single transaction, it likely is necessary to closely integrate with the machinery in \u003ccode\u003esrc/backend/access/transam/xlog.c\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAny developer of a new \u003ccode\u003etable access method\u003c/code\u003e can refer to the existing \u003ccode\u003eheap\u003c/code\u003e implementation present in \u003ccode\u003esrc/backend/access/heap/heapam_handler.c\u003c/code\u003e for details of its implementation.\u003c/p\u003e\n\u003c/div\u003e","SourceRevision":"555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f","ContentHash":"2b2229cefdc73e95b72d3fd7c31b30de3a9507cb20a5d0553b399e0ad65fccfe","Payload":{"description":["The built-in heap table access method integrates tuple storage, visibility, scans, index fetches and maintenance with PostgreSQL."],"manual_html":"\u003cdiv class=\"chapter\" id=\"TABLEAM\"\u003e\n\u003cdiv class=\"titlepage\"\u003e\n\u003cdiv\u003e\n\u003cdiv\u003e\n\u003ch2 class=\"title\"\u003eChapter 62. Table Access Method Interface Definition\u003c/h2\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003cp\u003eThis chapter explains the interface between the core \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e system and \u003cem class=\"firstterm\"\u003etable access methods\u003c/em\u003e, which manage the storage for tables. The core system knows little about these access methods beyond what is specified here, so it is possible to develop entirely new access method types by writing add-on code.\u003c/p\u003e\n\u003cp\u003eEach table access method is described by a row in the \u003ca class=\"link\" href=\"/docs/18/catalog-pg-am.html\" title=\"52.3. pg_am\"\u003e\u003ccode class=\"structname\"\u003epg_am\u003c/code\u003e\u003c/a\u003e system catalog. The \u003ccode class=\"structname\"\u003epg_am\u003c/code\u003e entry specifies a name and a \u003cem class=\"firstterm\"\u003ehandler function\u003c/em\u003e for the table access method. These entries can be created and deleted using the \u003ca class=\"xref\" href=\"/docs/18/sql-create-access-method.html\" title=\"CREATE ACCESS METHOD\"\u003e\u003cspan class=\"refentrytitle\"\u003eCREATE ACCESS METHOD\u003c/span\u003e\u003c/a\u003e and \u003ca class=\"xref\" href=\"/docs/18/sql-drop-access-method.html\" title=\"DROP ACCESS METHOD\"\u003e\u003cspan class=\"refentrytitle\"\u003eDROP ACCESS METHOD\u003c/span\u003e\u003c/a\u003e SQL commands.\u003c/p\u003e\n\u003cp\u003eA table access method handler function must be declared to accept a single argument of type \u003ccode class=\"type\"\u003einternal\u003c/code\u003e and to return the pseudo-type \u003ccode class=\"type\"\u003etable_am_handler\u003c/code\u003e. The argument is a dummy value that simply serves to prevent handler functions from being called directly from SQL commands.\u003c/p\u003e\n\u003cp\u003eHere is how an extension SQL script file might create a table access method handler:\u003c/p\u003e\n\u003cpre class=\"programlisting\"\u003eCREATE OR REPLACE FUNCTION my_tableam_handler(internal)\n  RETURNS table_am_handler AS 'my_extension', 'my_tableam_handler'\n  LANGUAGE C STRICT;\n\nCREATE ACCESS METHOD myam TYPE TABLE HANDLER my_tableam_handler;\n\u003c/pre\u003e\n\u003cp\u003eThe result of the function must be a pointer to a struct of type \u003ccode class=\"structname\"\u003eTableAmRoutine\u003c/code\u003e, which contains everything that the core code needs to know to make use of the table access method. The return value needs to be of server lifetime, which is typically achieved by defining it as a \u003ccode class=\"literal\"\u003estatic const\u003c/code\u003e variable in global scope.\u003c/p\u003e\n\u003cp\u003eHere is how a source file with the table access method handler might look like:\u003c/p\u003e\n\u003cpre class=\"programlisting\"\u003e#include \"postgres.h\"\n\n#include \"access/tableam.h\"\n#include \"fmgr.h\"\n\nPG_MODULE_MAGIC;\n\nstatic const TableAmRoutine my_tableam_methods = {\n    .type = T_TableAmRoutine,\n\n    /* Methods of TableAmRoutine omitted from example, add them here. */\n};\n\nPG_FUNCTION_INFO_V1(my_tableam_handler);\n\nDatum\nmy_tableam_handler(PG_FUNCTION_ARGS)\n{\n    PG_RETURN_POINTER(\u0026amp;my_tableam_methods);\n}\n\n\u003c/pre\u003e\n\u003cp\u003eThe \u003ccode class=\"structname\"\u003eTableAmRoutine\u003c/code\u003e struct, also called the access method's \u003cem class=\"firstterm\"\u003eAPI struct\u003c/em\u003e, defines the behavior of the access method using callbacks. These callbacks are pointers to plain C functions and are not visible or callable at the SQL level. All the callbacks and their behavior is defined in the \u003ccode class=\"structname\"\u003eTableAmRoutine\u003c/code\u003e structure (with comments inside the struct defining the requirements for callbacks). Most callbacks have wrapper functions, which are documented from the point of view of a user (rather than an implementor) of the table access method. For details, please refer to the \u003ca class=\"ulink\" href=\"https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/access/tableam.h;hb=HEAD\"\u003e\u003ccode class=\"filename\"\u003esrc/include/access/tableam.h\u003c/code\u003e\u003c/a\u003e file.\u003c/p\u003e\n\u003cp\u003eTo implement an access method, an implementor will typically need to implement an AM-specific type of tuple table slot (see \u003ca class=\"ulink\" href=\"https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/executor/tuptable.h;hb=HEAD\"\u003e\u003ccode class=\"filename\"\u003esrc/include/executor/tuptable.h\u003c/code\u003e\u003c/a\u003e), which allows code outside the access method to hold references to tuples of the AM, and to access the columns of the tuple.\u003c/p\u003e\n\u003cp\u003eCurrently, the way an AM actually stores data is fairly unconstrained. For example, it's possible, but not required, to use postgres' shared buffer cache. In case it is used, it likely makes sense to use \u003cspan class=\"productname\"\u003ePostgreSQL\u003c/span\u003e's standard page layout as described in \u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html\" title=\"66.6. Database Page Layout\"\u003eSection 66.6\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eOne fairly large constraint of the table access method API is that, currently, if the AM wants to support modifications and/or indexes, it is necessary for each tuple to have a tuple identifier (TID) consisting of a block number and an item number (see also \u003ca class=\"xref\" href=\"/docs/18/storage-page-layout.html\" title=\"66.6. Database Page Layout\"\u003eSection 66.6\u003c/a\u003e). It is not strictly necessary that the sub-parts of TIDs have the same meaning they e.g., have for \u003ccode class=\"literal\"\u003eheap\u003c/code\u003e, but if bitmap scan support is desired (it is optional), the block number needs to provide locality.\u003c/p\u003e\n\u003cp\u003eFor crash safety, an AM can use postgres' \u003ca class=\"link\" href=\"/docs/18/wal.html\" title=\"Chapter 28. Reliability and the Write-Ahead Log\"\u003eWAL\u003c/a\u003e, or a custom implementation. If WAL is chosen, either \u003ca class=\"link\" href=\"/docs/18/generic-wal.html\" title=\"64.1. Generic WAL Records\"\u003eGeneric WAL Records\u003c/a\u003e can be used, or a \u003ca class=\"link\" href=\"/docs/18/custom-rmgr.html\" title=\"64.2. Custom WAL Resource Managers\"\u003eCustom WAL Resource Manager\u003c/a\u003e can be implemented.\u003c/p\u003e\n\u003cp\u003eTo implement transactional support in a manner that allows different table access methods be accessed within a single transaction, it likely is necessary to closely integrate with the machinery in \u003ccode class=\"filename\"\u003esrc/backend/access/transam/xlog.c\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eAny developer of a new \u003ccode class=\"literal\"\u003etable access method\u003c/code\u003e can refer to the existing \u003ccode class=\"literal\"\u003eheap\u003c/code\u003e implementation present in \u003ccode class=\"filename\"\u003esrc/backend/access/heap/heapam_handler.c\u003c/code\u003e for details of its implementation.\u003c/p\u003e\n\u003c/div\u003e","related":[{"label":"System catalog pg_am","url":"/wiki/catalog/pg_am/?v=18"},{"label":"default_table_access_method","url":"/wiki/guc/default_table_access_method/?v=18"},{"label":"Index AM","url":"/wiki/indexam/?v=18"}],"sections":[{"paragraphs":["The matrix records callbacks actually registered in this source build. Registration identifies an implemented interface hook; options, query shape, privileges and provider rules determine whether an operation is allowed.","The complete same-version manual below retains configuration, constraints and examples. No runtime capability test is claimed."],"title":"Interface and capability boundaries"},{"paragraphs":["This inventory starts with the Table AM interface in PostgreSQL 12. Earlier PostgreSQL heap storage is not relabeled as a Table AM implementation.","Heap visibility follows PostgreSQL snapshots. WAL and crash-recovery behavior depend on relation persistence; temporary and unlogged tables differ from permanent tables. Index access methods remain separate from the table storage interface."],"title":"Heap storage, visibility and WAL"},{"code":"{\n\t.type = T_TableAmRoutine,\n\n\t.slot_callbacks = heapam_slot_callbacks,\n\n\t.scan_begin = heap_beginscan,\n\t.scan_end = heap_endscan,\n\t.scan_rescan = heap_rescan,\n\t.scan_getnextslot = heap_getnextslot,\n\n\t.scan_set_tidrange = heap_set_tidrange,\n\t.scan_getnextslot_tidrange = heap_getnextslot_tidrange,\n\n\t.parallelscan_estimate = table_block_parallelscan_estimate,\n\t.parallelscan_initialize = table_block_parallelscan_initialize,\n\t.parallelscan_reinitialize = table_block_parallelscan_reinitialize,\n\n\t.index_fetch_begin = heapam_index_fetch_begin,\n\t.index_fetch_reset = heapam_index_fetch_reset,\n\t.index_fetch_end = heapam_index_fetch_end,\n\t.index_fetch_tuple = heapam_index_fetch_tuple,\n\n\t.tuple_insert = heapam_tuple_insert,\n\t.tuple_insert_speculative = heapam_tuple_insert_speculative,\n\t.tuple_complete_speculative = heapam_tuple_complete_speculative,\n\t.multi_insert = heap_multi_insert,\n\t.tuple_delete = heapam_tuple_delete,\n\t.tuple_update = heapam_tuple_update,\n\t.tuple_lock = heapam_tuple_lock,\n\n\t.tuple_fetch_row_version = heapam_fetch_row_version,\n\t.tuple_get_latest_tid = heap_get_latest_tid,\n\t.tuple_tid_valid = heapam_tuple_tid_valid,\n\t.tuple_satisfies_snapshot = heapam_tuple_satisfies_snapshot,\n\t.index_delete_tuples = heap_index_delete_tuples,\n\n\t.relation_set_new_filelocator = heapam_relation_set_new_filelocator,\n\t.relation_nontransactional_truncate = heapam_relation_nontransactional_truncate,\n\t.relation_copy_data = heapam_relation_copy_data,\n\t.relation_copy_for_cluster = heapam_relation_copy_for_cluster,\n\t.relation_vacuum = heap_vacuum_rel,\n\t.scan_analyze_next_block = heapam_scan_analyze_next_block,\n\t.scan_analyze_next_tuple = heapam_scan_analyze_next_tuple,\n\t.index_build_range_scan = heapam_index_build_range_scan,\n\t.index_validate_scan = heapam_index_validate_scan,\n\n\t.relation_size = table_block_relation_size,\n\t.relation_needs_toast_table = heapam_relation_needs_toast_table,\n\t.relation_toast_am = heapam_relation_toast_am,\n\t.relation_fetch_toast_slice = heap_fetch_toast_slice,\n\n\t.relation_estimate_size = heapam_estimate_rel_size,\n\n\t.scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,\n\t.scan_sample_next_block = heapam_scan_sample_next_block,\n\t.scan_sample_next_tuple = heapam_scan_sample_next_tuple\n}","title":"Registered implementation in core source"}],"tables":[{"columns":[{"key":"feature","label":"Interface operation"},{"key":"state","label":"Source observation"},{"key":"callback","label":"Callback"},{"key":"implementation","label":"Implementation"}],"key":"callbacks","rows":[{"callback":"slot_callbacks","feature":"Tuple slot","implementation":"heapam_slot_callbacks","state":"Handler registered; conditions apply"},{"callback":"scan_begin","feature":"Begin scan","implementation":"heap_beginscan","state":"Handler registered; conditions apply"},{"callback":"parallelscan_initialize","feature":"Parallel scan initialization","implementation":"table_block_parallelscan_initialize","state":"Handler registered; conditions apply"},{"callback":"index_fetch_begin","feature":"Begin index lookup","implementation":"heapam_index_fetch_begin","state":"Handler registered; conditions apply"},{"callback":"index_fetch_tuple","feature":"Fetch identified tuple","implementation":"heapam_index_fetch_tuple","state":"Handler registered; conditions apply"},{"callback":"tuple_insert","feature":"Insert tuple","implementation":"heapam_tuple_insert","state":"Handler registered; conditions apply"},{"callback":"tuple_update","feature":"Update tuple","implementation":"heapam_tuple_update","state":"Handler registered; conditions apply"},{"callback":"tuple_delete","feature":"Delete tuple","implementation":"heapam_tuple_delete","state":"Handler registered; conditions apply"},{"callback":"relation_vacuum","feature":"Vacuum relation","implementation":"heap_vacuum_rel","state":"Handler registered; conditions apply"},{"callback":"scan_analyze_next_block","feature":"Analyze block","implementation":"heapam_scan_analyze_next_block","state":"Handler registered; conditions apply"},{"callback":"relation_needs_toast_table","feature":"TOAST decision","implementation":"heapam_relation_needs_toast_table","state":"Handler registered; conditions apply"}],"title":"Registered interface handlers"}]}},"RequestedLocale":"zh-Hans","Fallback":true,"Versions":["12","13","14","15","16","17","18","19","20"],"Locales":["en"],"Signatures":null,"Spellings":null,"SQLState":null,"Evidence":null}
