目录头文件的关键部分是描述目录每一行布局的 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
会处理这项琐事。)如果可能,请避免使新目录成为引导目录。
如果您发现文档中任何不正确、与特定功能的体验不符或需要进一步澄清的地方,请使用此表格来报告文档问题。