受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

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 中被标记出来。如果目录列是定宽的,且其前面没有任何可空列或变宽列,bootstrap 代码会自动把它标记为 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 头文件中。

有少数目录基础到这样的程度,以致它们甚至不能通过大多数目录所使用的 BKI create 命令来创建,因为该命令需要向这些目录中写入信息来描述新目录。这些目录称为bootstrap目录,而定义这样的目录需要额外做很多工作:你必须在 pg_classpg_type 的预装载内容中手工为它们准备合适的条目,并且当该目录的结构后续发生变化时,还需要更新这些条目。(bootstrap 目录在 pg_attribute 中也需要预装载条目,不过幸运的是,如今 genbki.pl 会处理这件事。)如果可能的话,应尽量避免让新目录成为 bootstrap 目录。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。