70.2. Исходные данные системных каталогов

Для каждого каталога, с которым связаны вручную создаваемые исходные данные, (не все каталоги такие) имеется соответствующий файл .dat, содержащий эти данные в редактируемом формате.

70.2.1. Формат файла данных

Каждый файл .dat содержит описания структур данных Perl, в результате вычисления которых (функцией eval) в памяти формируется структура данных, состоящая из массива хеш-ссылок, соответствующих каждой строке каталога. Немного модифицированная выдержка из pg_database.dat иллюстрирует основные моменты:

[

# Здесь мог быть комментарий.
{ 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_' },

]

Замечания:

  • Общий формат файла: открывающая квадратная скобка, один или более наборов фигурных скобок, каждый из которых представляет строку каталога, и закрывающая квадратная скобка. После каждой закрывающей фигурной скобки должна идти запятая.

  • В каждой строке каталога записываются разделённые запятыми пары ключ => значение. В качестве ключа принимаются имена столбцов каталога, а также ключи метаданных oid, oid_symbol и descr. (Использование oid и oid_symbol описывается в Подразделе 70.2.2. В descr задаётся строка с описанием объекта, которое будет вставлено в pg_description или pg_shdescription.) Ключи метаданных могут опускаться, но ключ для каждого столбца каталога должен присутствовать, если только в файле .h данного каталога для столбца не задано значение по умолчанию.

  • Все значения должны заключаться в апострофы. Апострофы внутри значений экранируются обратной косой чертой. Обратные косые черты в данных могут, но не обязательно должны дублироваться; это соответствует правилам Perl по оформлению простых строковых констант. Заметьте, что обратные косые черты, фигурирующие в данных, будут обрабатываться сканером исходных данных как символы экранирования, согласно тем же правилам записи строковых констант (см. Подраздел 4.1.2.2); например, \t преобразуется в символ табуляции. Если вы хотите получить именно обратную косую черту в окончательном значении, вам надо будет написать четыре этих символа: Perl отбрасывает два и оставляет \\ сканеру исходных данных.

  • Значения NULL представляются как _null_. (Заметьте, что создать значение с именно такой строкой невозможно.)

  • Комментарии предваряются знаком # и должны размещаться в отдельных строках.

  • Для большей наглядности значения полей, выражающие OID других записей каталога, могут быть представлены именами, а не только числовыми кодами OID. Об этом рассказывается в Подразделе 70.2.3.

  • Так как хеши являются неупорядоченной структурой данных, порядок полей и расположение строк не имеют семантической значимости. Однако для поддержания согласованного представления мы установили несколько правил, которые применяет скрипт форматирования reformat_dat_file.pl:

    • В каждой паре фигурных скобок сначала идут поля метаданных oid, oid_symbol и descr, в этом же порядке, а затем собственные поля каталога в определённом для них порядке.

    • Переводы строк при необходимости вставляются между полями для ограничения длины строки 80 символами, если это возможно. Перевод строки также вставляется между полями метаданных и обычными полями.

    • Если в файле .h каталога задаётся значение по умолчанию для столбца и то же значение указано в записи данных, reformat_dat_file.pl уберёт его из файла данных. Таким образом обеспечивается компактное представление данных.

    • Скрипт reformat_dat_file.pl сохраняет пустые строки и строки комментариев в неизменном виде.

    Скрипт reformat_dat_file.pl рекомендуется запускать перед сохранением изменений в данных каталога. Им удобно пользоваться, просто выполняя make reformat-dat-files в src/include/catalog/.

  • Если вы хотите добавить новый метод уменьшения представления данных, вы должны реализовать его в reformat_dat_file.pl и также научить Catalog::ParseData() разворачивать данные в полное представление.

70.2.2. Назначение OID

Строке каталога, фигурирующей в исходных данных, можно вручную присвоить OID, добавив поле метаданных oid => nnnn. Более того, когда строке присваивается OID, для этого OID можно создать макрос C, добавив поле метаданных oid_symbol => имя.

Предварительно загружаемым строкам каталога должны заранее назначаться OID, если на них по OID ссылаются другие предварительно загружаемые строки. Назначать OID также требуется, если на OID нужно будет ссылаться из кода на C. В отсутствие этих условий поле метаданных oid можно опустить и тогда загрузочный код назначит OID автоматически либо оставит его нулевым, если OID в данном каталоге не используются. На практике мы обычно явно назначаем OID для всех строк в определённом каталоге (даже если фактически присутствуют ссылки только на часть из них) либо не назначаем их вовсе.

Указание фактического числового значения любого OID в коде на C считается крайне нежелательным; вместо этого всегда следует использовать макрос. Прямые обращения к OID в pg_proc требуются достаточно часто, поэтому был создан специальный механизм, создающий необходимые макросы автоматически; см. src/backend/utils/Gen_fmgrtab.pl. С аналогичной целью предусмотрен (но по историческим причинам реализован по-другому) метод создания макросов для OID в pg_type. Как следствие, записи 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 или больше.

70.2.3. Поиск OID по ссылке

Перекрёстную ссылку из одной строки исходного каталога на другую можно записать, просто указав предопределённый OID целевой строки. Однако этот подход провоцирует ошибки и сложен для понимания, поэтому для часто используемых каталогов в genbki.pl реализованы механизмы записи символических ссылок. В настоящее время по символическим ссылкам можно обращаться к методам доступа, функциям, операторам, классам и семействам операторов, а также типам. При этом действуют следующие правила:

  • Для использования символических ссылок в некотором столбце каталога требуется добавить указание BKI_LOOKUP(правило_поиска) в определение этого столбца, где правилом_поиска может быть pg_am, pg_proc, pg_operator, pg_opclass, pg_opfamily или pg_type. Указание BKI_LOOKUP может быть добавлено к столбцам типа Oid, regproc, oidvector или Oid[]; в последних двух случаях поиск будет выполняться для каждого элемента массива.

  • В таком столбце все записи должны иметь символьный формат (исключение составляет 0, обозначающий InvalidOid). (Если столбец объявлен как regproc, вместо 0 можно написать -.) Скрипт genbki.pl выдаст предупреждение, встретив нераспознанное имя.

  • Методы доступа, как и типы, представляются просто своими именами. Имена типов должны соответствовать полям typname в соответствующих записях pg_type; псевдонимы типов использовать нельзя, например, нельзя написать integer вместо int4.

  • Функция может быть представлена своим значением proname, если оно уникально среди записей pg_proc.dat (это работает как ввод значения типа regproc). В противном случае её нужно представить как proname(имя_типа_аргумента,имя_типа_аргумента,...), как в regprocedure. Имена типов аргументов должны записываться в точности так, как они фигурируют в поле proargtypes в pg_proc.dat. Не добавляйте в эту строку пробелы.

  • Операторы представляются в виде oprname(левый_тип,правый_тип), при этом имена типов записываются в точности так, как они фигурируют в полях oprleft и oprright в pg_operator.dat. (Вместо опущенного операнда унарного оператора записывается 0.)

  • Имена классов операторов и семейств операторов уникальны только в рамках определённого метода доступа, так что они представляются в виде имя_метода_доступа/имя_объекта.

  • Ни в одном из этих случаев не поддерживается указание схемы; все объекты, создаваемые на стадии начальной загрузки, будут принадлежать схеме pg_catalog.

Скрипт genbki.pl разрешает все символические ссылки при запуске и помещает в формируемый файл BKI обычные числовые OID. Таким образом, при начальной загрузке отпадает необходимость в разрешении имён.

70.2.4. Рецепты по редактированию файлов данных

Ниже приведены некоторые предложения по оптимальному решению некоторых распространённых задач при изменении файлов каталогов.

Добавление в каталог нового столбца со значением по умолчанию: Добавьте столбец в заголовочный файл с указанием BKI_DEFAULT(значение). Файл данных потребуется редактировать, только если в каких-либо существующих строках в добавленном поле должно быть не значение по умолчанию.

Указание значения по умолчанию для существующего столбца, который его не имел: Добавьте указание BKI_DEFAULT в заголовочный файл, а затем выполните make reformat-dat-files для удаления ставших избыточными записей поля.

Удаление столбца, со значением по умолчанию или без: Удалите столбец из заголовочного файла, а затем выполните make reformat-dat-files для удаления ставших избыточными записей поля.

Изменение или удаление существующего значения по умолчанию: Просто изменить заголовочный файл недостаточно, так как при этом текущие данные будут интерпретироваться некорректно. Сначала выполните make expand-dat-files, чтобы перезаписать в файлах данных все явно заданные значения по умолчанию, затем удалите или измените указания BKI_DEFAULT, и в завершение выполните make reformat-dat-files для повторного удаления избыточных полей.

Разовая массовая модификация: Скрипт reformat_dat_file.pl можно скорректировать для выполнения самых разных массовых модификаций. Просмотрев в нём блочные комментарии, вы найдёте место, куда можно вставить модифицирующий код. В следующем примере мы произведём объединение двух логических полей в pg_proc в символьном поле:

  1. Добавьте новый столбец со значением по умолчанию в pg_proc.h:

    +    /* see PROKIND_ categories below */
    +    char        prokind BKI_DEFAULT(f);
  2. Создайте на основе 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';
    +           }
  3. Запустите новый скрипт:

    $ cd src/include/catalog
    $ perl  rewrite_dat_with_prokind.pl  pg_proc.dat

    После этого в файле pg_proc.dat окажутся все три столбца, prokind, proisagg и proiswindow, хотя они будут фигурировать только в тех строках, где им присваиваются не значения по умолчанию, а любые другие значения.

  4. Удалите старые столбцы из pg_proc.h:

    -    /* is it an aggregate? */
    -    bool        proisagg BKI_DEFAULT(f);
    -
    -    /* is it a window function? */
    -    bool        proiswindow BKI_DEFAULT(f);
  5. Наконец, выполните make reformat-dat-files для удаления ненужных старых записей из pg_proc.dat.

Примеры кода, производящего массовые модификации, вы можете найти в скриптах convert_oid2name.pl и remove_pg_type_oid_symbols.pl, вложенных в сообщение: https://www.postgresql.org/message-id/CAJVSVGVX8gXnPm+Xa=DxR7kFYprcQ1tNcCT5D0O3ShfnM6jehA@mail.gmail.com