Skip to content

75.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#define和客户端可能用到的任何其他数据。如果您希望目录头中的宏或其他代码对前端代码可见,请在该部分周围编写#ifdef EXPOSE_TO_CLIENT_CODE...#endif,以指示genbki.pl将该部分复制到pg_xxx_d.h头文件中。

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