69.2. 系统目录初始数据

每个具有任何手动创建的初始数据(有些则没有)的目录都有一个对应的.dat文件,该文件以可编辑的格式包含其初始数据。

69 .2.1. 资料 Files 格式

每个.dat文件都包含 Perl 数据结构 Literals,这些 Literals 被简单地评估为生成内存中的数据结构,该数据结构由一组哈希引用组成,每个目录行一个。 pg_database.dat的摘录略作修改后,将演示其主要功能:

[

# A comment could appear here.
{ oid => '1', oid_symbol => 'TemplateDbOid',
  descr => 'database\'s default template',
  datname => 'template1', datdba => 'PGUID', encoding => 'ENCODING',
  datcollate => 'LC_COLLATE', datctype => 'LC_CTYPE', datistemplate => 't',
  datallowconn => 't', datconnlimit => '-1', datlastsysoid => '0',
  datfrozenxid => '0', datminmxid => '1', dattablespace => '1663',
  datacl => '_null_' },

]

注意事项:

  • 总体文件布局为:方括号,一组或多组花括号,每组花括号代表一个目录行,方括号封闭。在每个右花括号后写一个逗号。

  • 在每个目录行中,写以逗号分隔的* key * => * value *对。允许的key *是目录列的名称,再加上元数据键oidoid_symboldescr。 (下面的Section 69.2.2中描述了oidoid_symbol的用法。descr提供了对象的描述字符串,将酌情插入到pg_descriptionpg_shdescription中。)尽管元数据键是可选的,但必须提供目录的已定义列,除非目录的.h文件为该列指定默认值。

  • 所有值都必须用单引号引起来。用反斜杠转义值中使用的单引号。反斜杠意味着数据可以但不必加倍;这遵循 Perl 的简单引用 Literals 规则。请注意,按照与转义字符串常量相同的规则(请参见Section 4.1.2.2),引导扫描程序会将反斜杠视为数据转义。例如\t转换为制表符。如果您实际上想在最终值中使用反斜杠,则需要编写其中的四个:Perl 去除两个,剩下\\供引导扫描程序查看。

  • 空值由_null_表示。 (请注意,无法创建只是该字符串的值.)

  • Comments 以#开头,并且必须单独显示。

  • 为了提高可读性,可以使用名称而不是数字 OID 来表示作为其他目录条目的 OID 的字段值。下面的Section 69.2.3中对此进行了描述。

  • 由于哈希是无序的数据结构,因此字段 Sequences 和行布局在语义上并不重要。但是,为了保持外观的一致性,我们设置了一些格式脚本reformat_dat_file.pl所应用的规则:

  • 在每对花括号中,元数据字段oidoid_symboldescr(如果存在)按照该 Sequences 排在最前面,然后目录的自身字段按其定义的 Sequences 显示。

    • 如果需要,可以在字段之间插入换行符,以将行长度限制为 80 个字符。在元数据字段和常规字段之间还会插入一个换行符。

    • 如果目录的.h文件为列指定了默认值,并且数据条目具有相同的值,则reformat_dat_file.pl将从数据文件中忽略它。这使数据表示紧凑。

    • reformat_dat_file.pl照原样保留空白行和 Comments 行。

建议在提交目录数据补丁之前运行reformat_dat_file.pl。为了方便起见,您只需更改为src/include/catalog/并运行make reformat-dat-files即可。

  • 如果要添加一种使数据表示形式更小的新方法,则必须在reformat_dat_file.pl中实现它,还必须教Catalog::ParseData()如何将数据扩展回完整的表示形式。

69 .2.2. OID 分配

通过写入oid => nnnn元数据字段,可以为出现在初始数据中的目录行提供手动分配的 OID。此外,如果分配了 OID,则可以通过写入oid_symbol => name元数据字段来创建该 OID 的 C 宏。

如果在其他预加载的行中有对它们的 OID 引用,则预加载的目录行必须具有预分配的 OID。如果必须从 C 代码中引用行的 OID,则还需要预分配的 OID。如果两种情况都不适用,则可以省略oid元数据字段,在这种情况下,引导程序代码会自动分配一个 OID,或者在没有 OID 的目录中将其保留为零。实际上,我们通常为给定目录中的所有预加载行或不为所有预加载行预分配 OID,即使实际上只有部分交叉引用也是如此。

用 C 代码写任何 OID 的实际数值被认为是非常糟糕的形式。始终使用宏代替。直接引用pg_proc OID 很常见,以至于有一种特殊的机制可以自动创建必要的宏。参见src/backend/utils/Gen_fmgrtab.pl。类似地-但由于历史原因,操作方式有所不同-存在一种自动方法来为pg_type OID 创建宏。因此,在这两个目录中不需要oid_symbol条目。同样,系统会自动设置pg_class OID 的系统目录和索引的宏。对于所有其他系统目录,您必须通过oid_symbol条目手动指定所需的任何宏。

要为新的预加载行找到可用的 OID,请运行脚本src/include/catalog/unused_oids。它打印未使用的 OID 的范围(例如,输出行“ 45-900”表示尚未分配 OID 45 至 900)。当前,OID 1-9999 保留用于手动分配。 unused_oids脚本仅浏览目录标题和.dat文件,以查看未出现的文件。您也可以使用duplicate_oids脚本检查错误。 (genbki.pl还将在编译时检测到重复的 OID.)

在引导运行开始时,OID 计数器从 10000 开始。如果目录行在需要 OID 的表中,但是oid字段未预先分配 OID,则它将收到 10000 或更高的 OID。

69 .2.3. OID 参考查询

从一个初始目录行到另一个初始目录行的交叉引用可以通过仅写入参考行的预分配 OID 来编写。但这容易出错并且难以理解,因此对于频繁引用的目录,genbki.pl提供了代替编写符号引用的机制。当前,这可以用于访问方法,函数,运算符,操作类,操作族和类型的引用。规则如下:

  • 通过将BKI_LOOKUP(lookuprule)附加到该列的定义,可以在特定目录列中使用符号引用,其中* lookuprule *是pg_ampg_procpg_operatorpg_opclasspg_opfamilypg_typeBKI_LOOKUP可以附加到OidregprocoidvectorOid[]类型的列上;在后两种情况下,它意味着对数组的每个元素执行查找。

  • 在这样的列中,除了为 InvalidOid 写入0之外,所有条目都必须使用符号格式。 (如果该列被声明为regproc,则可以选择写-而不是0.)genbki.pl会警告无法识别的名称。

  • 访问方法仅由其名称表示,与类型一样。类型名称必须与引用的pg_type条目的typname匹配;您不会对int4使用任何别名,例如integer

  • 如果函数在pg_proc.dat条目中是唯一的,则可以用其proname表示(这类似于 regprocImporting)。否则,将其写为* proname(argtypename,argtypename,...) *,就像 regprocedure 一样。参数类型名称的拼写必须与pg_proc.dat条目的proargtypes字段中的拼写完全相同。请勿插入任何空格。

  • 运算符由* oprname(lefttype,righttype) *表示,其类型名称应与在pg_operator.dat条目的oprleftoprright字段中显示的名称完全相同。 (将0写入一元运算符的省略操作数.)

  • opclass 和 opfamily 的名称仅在访问方法中唯一,因此由* access_method_name * / * object_name *表示。

  • 在所有这些情况下,都没有提供模式资格的规定;引导过程中创建的所有对象都应位于 pg_catalog 模式中。

genbki.pl在运行时解析所有符号引用,并将简单的数字 OID 放入发出的 BKI 文件中。因此,不需要引导后端处理符号引用。

69 .2.4. 用于编辑数据文件的食谱

以下是有关更新目录数据文件时执行常见任务的最简单方法的一些建议。

将默认的新列添加到目录: 使用BKI_DEFAULT(value)Comments 将该列添加到头文件中。只需通过在需要非默认值的现有行中添加字段来调整数据文件。

向没有一列的现有列添加默认值: 向头文件添加BKI_DEFAULT注解,然后运行make reformat-dat-files删除现在冗余的字段条目。

删除一列,不管它是否具有默认值: 从标题中删除该列,然后运行make reformat-dat-files删除现在无用的字段条目。

更改或删除现有的默认值: 您不能简单地更改头文件,因为这将导致当前数据被错误地解释。首先运行make expand-dat-files重写具有显式插入的所有默认值的数据文件,然后更改或删除BKI_DEFAULT注解,然后运行make reformat-dat-files再次删除多余的字段。

即席批量编辑: reformat_dat_file.pl可用于执行多种批量更改。查找其块 Comments,该 Comments 显示可以在其中插入一次性代码的位置。在下面的示例中,我们将把pg_proc中的两个布尔字段合并为一个 char 字段:

  • 将新列(默认情况下)添加到pg_proc.h
+    /* see PROKIND_ categories below */
+    char        prokind BKI_DEFAULT(f);
  • 创建一个基于reformat_dat_file.pl的新脚本,以即时插入适当的值:
-           # At this point we have the full row in memory as a hash
-           # and can do any operations we want. As written, it only
-           # removes default values, but this script can be adapted to
-           # do one-off bulk-editing.
+           # One-off change to migrate to prokind
+           # Default has already been filled in by now, so change to other
+           # values as appropriate
+           if ($values{proisagg} eq 't')
+           {
+               $values{prokind} = 'a';
+           }
+           elsif ($values{proiswindow} eq 't')
+           {
+               $values{prokind} = 'w';
+           }
  • 运行新脚本:
$ cd src/include/catalog
$ perl  rewrite_dat_with_prokind.pl  pg_proc.dat

此时,pg_proc.dat具有所有三列prokindproisaggproiswindow,尽管它们只会出现在具有非默认值的行中。

  • pg_proc.h删除旧列:
-    /* is it an aggregate? */
-    bool        proisagg BKI_DEFAULT(f);
-
-    /* is it a window function? */
-    bool        proiswindow BKI_DEFAULT(f);
  • 最后,运行make reformat-dat-filespg_proc.dat中删除无用的旧条目。

有关用于批量编辑的脚本的更多示例,请参阅此消息所附的convert_oid2name.plremove_pg_type_oid_symbols.pl