2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11

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_NULLBKI_FORCE_NULL 注解来强制正确标记。

前端代码不应包含任何 pg_xxx.h 目录头文件,因为这些文件可能包含在后端之外无法编译的 C 代码。(通常,这是因为这些文件还包含 src/backend/catalog/ 文件中函数的声明。)相反,前端代码可以包含相应的生成 pg_xxx_d.h 头文件,其中将包含 OID #defines 和可能在客户端有用的任何其他数据。如果您希望目录头文件中的宏或其他代码对前端代码可见,请将该部分用 #ifdef EXPOSE_TO_CLIENT_CODE ... #endif 括起来,以指示 genbki.pl 将该部分复制到 pg_xxx_d.h 头文件中。

一些目录非常基础,以至于它们甚至无法通过用于大多数目录的 create 命令创建,因为该命令需要向这些目录写入信息来描述新目录。这些被称为 引导 目录,定义它们需要大量额外工作:您必须手动准备预加载内容中包含的 pg_classpg_type 的相应条目,并且这些条目需要针对目录结构的后续更改进行更新。(引导目录还需要 pg_attribute 的预加载条目,但幸运的是 genbki.pl 现在处理了这项工作。)如果可能,请尽量避免创建新的引导目录。BKI create 命令,因为该命令需要向这些目录写入信息来描述新的目录。这些被称为 引导 目录,定义它们需要大量额外工作:您必须手动准备预加载内容中包含的 pg_classpg_type 的相应条目,并且这些条目需要针对目录结构的后续更改进行更新。(引导目录还需要 pg_attribute 的预加载条目,但幸运的是 genbki.pl 现在处理了这项工作。)如果可能,请尽量避免创建新的引导目录。

提交更正

如果您在文档中发现任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。