本章介绍 PostgreSQL 核心引擎与负责管理表物理存储的表访问方法(Table Access Methods)之间的扩展接口。核心引擎对底层存储引擎的具体细节知之甚少,因此开发者可以通过编写外部扩展完全定制全新的表存储引擎(例如列存、内存表或压缩追加存储)。
每个表访问方法均在系统目录表 pg_am 中作为一行记录进行描述。pg_am 条目指定了该访问方法的名称以及其对应的处理器函数(Handler Function)。可以使用 SQL 命令 CREATE ACCESS METHOD 与 DROP ACCESS METHOD 创建与删除这些条目。
表访问方法处理器函数必须声明为接受一个单一的 internal 伪类型参数,并返回伪类型 table_am_handler。该参数是一个虚拟占位符,主要用于防止该函数在 SQL 层被直接调用。
以下展示扩展 SQL 安装脚本中如何注册表访问方法处理器:
CREATE OR REPLACE FUNCTION my_tableam_handler(internal) RETURNS table_am_handler AS 'my_extension', 'my_tableam_handler' LANGUAGE C STRICT; CREATE ACCESS METHOD myam TYPE TABLE HANDLER my_tableam_handler;
该 C 语言处理函数的返回值必须是一个指向类型为 TableAmRoutine 的结构体指针,该结构体包含了内核使用该存储引擎所需的全部回调函数指针。返回值必须具备服务端全局生命周期,通常在 C 全局作用域中定义为 static const 常量变量。
以下展示该 C 源文件可能的实现形式:
#include "postgres.h"
#include "access/tableam.h"
#include "fmgr.h"
PG_MODULE_MAGIC;
static const TableAmRoutine my_tableam_methods = {
.type = T_TableAmRoutine,
/* 在此处填入 TableAmRoutine 要求的具体回调函数指针 */
};
PG_FUNCTION_INFO_V1(my_tableam_handler);
Datum
my_tableam_handler(PG_FUNCTION_ARGS)
{
PG_RETURN_POINTER(&my_tableam_methods);
}
结构体 TableAmRoutine(亦称为访问方法的 API 结构体)通过回调函数精确界定了该存储引擎的物理行为。这些回调均为原生 C 函数指针,在 SQL 层面不可见且无法直接调用。所有回调及其接口契约均在头文件 src/include/access/tableam.h 中详细定义。
为了实现一个访问方法,开发者通常需要实现专属的元组表槽类型(Tuple Table Slot,参见 src/include/executor/tuptable.h),允许外部执行器持有对该 AM 中元组的引用并提取列字段。
目前系统对访问方法实际如何物理存储数据并未施加过多强制约束。例如可以使用(但不强制)PostgreSQL 共享缓冲区池(Shared Buffer Pool)。在采用共享缓冲区时,通常推荐遵循 Section 66.6 中所述的标准页面物理布局。
表访问方法 API 当前的一大约束是:若该 AM 需要支持数据修改(UPDATE/DELETE)或辅助索引,每个元组必须具备由块号(Block Number)与项偏移号(Item Offset)构成的元组标识符(TID,参见 Section 66.6)。若需要支持位图扫描(Bitmap Scan),块号必须具备物理局部性。
对于崩溃安全性,访问方法可以直接复用官方预写式日志 WAL,也可以采用自定义存储机制。若采用 WAL,既可使用通用 WAL 记录,也可实现自定义 WAL 资源管理器。
为了在单次事务中无缝混用不同的表访问方法,事务支持需与底层事务协调引擎深度集成。开发者可以参考内核中默认内置的堆存储引擎实现:src/backend/access/heap/heapam_handler.c。