PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

68.1. 系统目录声明规则 #

目录头文件的关键部分是一个描述目录每一行布局的 C 结构定义。 它以一个 CATALOG 宏开始,就 C 编译器而言它只是 typedef struct FormData_catalogname 的 速记。结构中的每个字段都产生一个目录列。 字段可以使用 genbki.h 中描述的 BKI 属性宏 加以注解,例如为字段定义默认值,或把它标记为可为空或不可为空。 CATALOG 行也可以用 genbki.h 中描述的其他一些 BKI 属性宏注解, 以定义目录整体的其他属性,例如它是否是共享关系。

系统目录缓存代码(以及大多数目录处理代码)假定所有系统目录元组的 定长部分确实存在,因为它把这个 C 结构声明映射到这些元组上。 因此,所有变长字段和可空字段都必须放在末尾,并且不能作为结构字段 访问。 例如,如果您试图把 pg_type.typrelid 设为 NULL,那么当某段代码试图引用 typetup->typrelid 时就会失败(更糟的是 typetup->typelem,因为它跟在 typrelid 后面)。 这会导致随机错误甚至段错误。

作为对这类错误的部分防护,变长字段或可空字段不应对 C 编译器直接 可见。这是通过把它们包装在 #ifdef CATALOG_VARLEN ... #endif 中实现的 (其中 CATALOG_VARLEN 是一个从不被定义的符号)。 这可以防止 C 代码不小心访问可能不存在、或可能位于其他偏移处的 字段。 作为防止创建不正确行的独立防护,我们要求所有应当不可为空的列都在 pg_attribute 中被标记为如此。 如果目录列是定宽的,并且前面没有任何可空或变宽的列,引导代码会 自动把它们标记为 NOT NULL。 在这条规则不够用的地方,您可以根据需要使用 BKI_FORCE_NOT_NULL 和 BKI_FORCE_NULL 注解来强制正确的标记。

前端代码不应包含任何 pg_xxx.h 目录头文件,因为这些文件可能包含在后端之外无法编译的 C 代码。 (典型的情况是,这些文件还包含 src/backend/catalog/ 文件中函数的声明。) 前端代码可以改为包含相应的已生成 pg_xxx_d.h 头文件,其中包含 OID #define 以及其他任何可能在客户端使用的 数据。如果您希望目录头文件中的宏或其他代码对前端代码可见,请在该 部分前后写上 #ifdef EXPOSE_TO_CLIENT_CODE ... #endif, 以指示 genbki.pl 把该部分复制到 pg_xxx_d.h 头文件中。

少数目录非常根本,以至于它们甚至无法通过用于大多数目录的 BKI create 命令来创建, 因为该命令需要向这些目录写入信息来描述新目录。 这些目录被称为引导目录,定义一个引导目录 需要大量额外工作:您必须手工为它们在 pg_class 和 pg_type 的预载内容中准备适当的条目,而且 这些条目在目录结构随后变化时需要更新。 (引导目录还需要在 pg_attribute 中有预载 条目,不过幸运的是如今 genbki.pl 会处理这项工作。) 如果可能,应避免让新目录成为引导目录。