目录头文件中的关键部分是描述目录每一行布局的 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
s 和可能在客户端有用的任何其他数据。如果您希望目录头文件中的宏或其他代码对前端代码可见,请将该部分用 #ifdef EXPOSE_TO_CLIENT_CODE
... #endif
括起来,以指示 genbki.pl
将该部分复制到 pg_xxx_d.h
头文件中。
一些目录非常基础,以至于它们甚至无法通过用于大多数目录的 create
命令创建,因为该命令需要向这些目录写入信息来描述新目录。这些被称为 引导 目录,定义它们需要大量额外工作:您必须手动准备预加载内容中包含的 pg_class
和 pg_type
的相应条目,并且这些条目需要针对目录结构的后续更改进行更新。(引导目录还需要 pg_attribute
的预加载条目,但幸运的是 genbki.pl
现在处理了这项工作。)如果可能,请尽量避免创建新的引导目录。BKI create
命令,因为该命令需要向这些目录写入信息来描述新的目录。这些被称为 引导 目录,定义它们需要大量额外工作:您必须手动准备预加载内容中包含的 pg_class
和 pg_type
的相应条目,并且这些条目需要针对目录结构的后续更改进行更新。(引导目录还需要 pg_attribute
的预加载条目,但幸运的是 genbki.pl
现在处理了这项工作。)如果可能,请尽量避免创建新的引导目录。
如果您在文档中发现任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。