Перейти к основному содержимому

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

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

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

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

[

# A comment could appear here.
{ oid => '1', oid_symbol => 'Template1DbOid',
descr => 'database\'s default template',
datname => 'template1', encoding => 'ENCODING',
datlocprovider => 'LOCALE_PROVIDER', datistemplate => 't',
datallowconn => 't', datconnlimit => '-1', datfrozenxid => '0',
datminmxid => '1', dattablespace => 'pg_default', datcollate => 'LC_COLLATE',
datctype => 'LC_CTYPE', daticulocale => 'ICU_LOCALE', datacl => '_null_' },

]

Обратите внимание:

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

  • В каждой строке каталога запишите пары ключ => значение, разделенные запятыми. Допустимыми ключами являются имена столбцов каталога, а также ключи метаданных oid, oid_symbol, array_type_oid и descr. (Использование oid и oid_symbol описано ниже в разделе «Назначение OID», а array_type_oid — в разделе «Автоматическое создание типов массивов». descr предоставляет строку описания объекта, которая будет вставлена в pg_description или pg_shdescription в зависимости от ситуации). Хотя ключи метаданных необязательны, все определенные столбцы каталога должны быть указаны, за исключением случаев, когда в файле .h каталога указано значение по умолчанию для столбца. (В приведенном выше примере поле datdba было опущено, поскольку pg_database.h предоставляет для него подходящее значение по умолчанию).

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

  • Нулевые значения обозначаются символом \_null_. (Обратите внимание, что нет способа создать значение, которое было бы просто этой строкой).

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

  • Значения полей, которые являются OID других записей каталога, должны быть представлены символическими именами, а не фактическими числовыми OID. (В приведенном выше примере dattablespace содержит такую ссылку.) Это описано в разделе «Поиск ссылок на OID» ниже.

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

    • Внутри каждой пары фигурных скобок сначала идут поля метаданных oid, oid_symbol, array_type_oid и descr (если они есть), а затем собственные поля каталога в определенном порядке.
    • Между полями по мере необходимости вставляются новые строки, чтобы по возможности ограничить длину строки до 80 символов. Новая строка также вставляется между полями метаданных и обычными полями.
    • Если в .h-файле каталога указано значение по умолчанию для столбца, а в записи данных присутствует это значение, reformat_dat_file.pl опустит его из файла данных. Это позволяет сохранить компактность представления данных.
    • reformat_dat_file.pl сохраняет пустые строки и строки комментариев как есть.

Рекомендуется запускать reformat\_dat\_file.pl перед отправкой патчей данных каталога. Для удобства можно просто изменить на src/include/catalog/ и запустить make reformat-dat-files.

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

Назначение OID

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

Предварительно загружаемым строкам каталога должны заранее назначаться OID, если на них по OID ссылаются другие предварительно загружаемые строки. Назначать OID также требуется, если на OID нужно будет ссылаться из кода на C. В отсутствие этих условий поле метаданных 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 во время компиляции).

При выборе OID для патча, который, как ожидается, не будет зафиксирован сразу, лучшая практика — использовать группу более или менее последовательных OID, начиная с некоторого случайного выбора в диапазоне 8000-9999. Это минимизирует риск столкновения OID с другими патчами, разрабатываемыми одновременно. Чтобы сохранить диапазон 8000-9999 свободным для целей разработки, после того, как патч зафиксирован в главном репозитории git, его OID должны быть перенумерованы в доступное пространство ниже этого диапазона. Обычно это делается к концу каждого цикла разработки, перемещая все OID, потребляемые патчами, зафиксированными в этом цикле, одновременно. Для этого можно использовать сценарий renumber_oids.pl. Если будет обнаружено, что незафиксированный патч имеет конфликт OID с недавно зафиксированным патчем, renumber_oids.pl также может быть полезен для восстановления после этой ситуации.

Из-за этого соглашения о возможном перенумеровании OID, назначенных патчами, OID, назначенные патчем, не должны считаться стабильными, пока патч не будет включен в официальный релиз. Однако мы не меняем OID объектов, назначенных вручную, после выпуска, поскольку это создаст различные проблемы с совместимостью.

Если genbki.pl нужно присвоить OID элементу каталога, у которого нет назначенного вручную OID, он будет использовать значение в диапазоне 10000-11999. Счетчик OID сервера устанавливается на 10000 в начале загрузки, так что любые объекты, созданные «на лету» во время загрузки, также получают OID в этом диапазоне. (Обычный механизм назначения OID позаботится о предотвращении любых конфликтов).

Объекты с OID ниже FirstUnpinnedObjectId (12000) считаются «прикрепленными», что предотвращает их удаление. (Есть небольшое количество исключений, которые жестко прописаны в IsPinnedObject().) initdb заставляет счетчик OID подняться до FirstUnpinnedObjectId, как только он готов создавать неприкрепленные объекты. Таким образом, объекты, созданные на более поздних этапах initdb, например, объекты, созданные при выполнении скрипта information_schema.sql, не будут привязаны, в то время как все объекты, известные genbki.pl, будут привязаны.

OID, назначаемые во время нормальной работы базы данных, ограничены 16384 или выше. Это гарантирует, что диапазон 10000-16383 будет свободен для OID, назначенных автоматически с помощью genbki.pl или во время initdb. Эти автоматически назначенные OID не считаются стабильными и могут меняться от одной установки к другой.

Поиск ссылок на OID

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

  • Использование символических ссылок разрешается в определенной колонке каталога путем присоединения BKI_LOOKUP(правило_поиска) к определению столбца, где правило_поиска — имя целевого каталога, например pg_proc. Указание BKI_LOOKUP может быть добавлено к столбцам типа Oid, regproc, oidvector или Oid[]; в последних двух случаях поиск будет выполняться для каждого элемента массива.

  • Также допускается указание BKI_LOOKUP(encoding) для целочисленных столбцов, позволяющее обращаться к кодировкам символов (в настоящее время кодировкам не соответствуют OID в каталоге, но скрипту genbki.pl известен их набор).

  • Для некоторых столбцов каталога в качестве значений могут допускаться не только корректные ссылки, но и нули. Для таких столбцов вместо BKI_LOOKUP укажите BKI_LOOKUP_OPT. Это позволит задать 0 в качестве значения столбца. (Если столбец объявлен как regproc, можно написать - вместо 0.) За исключением этого особого случая, все записи в столбце BKI_LOOKUP должны содержать символьные ссылки. 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 разрешает все символические ссылки во время своей работы и помещает простые числовые OID в создаваемый BKI-файл. Поэтому при начальной загрузке отпадает необходимость в разрешении имен.

Желательно помечать столбцы ссылок OID значением BKI_LOOKUP или BKI_LOOKUP_OPT, даже если в каталоге нет исходных данных, требующих поиска. Это позволяет genbki.pl регистрировать отношения внешних ключей, существующие в каталогах системы. Эта информация применяется в регрессионных тестах для выявления неверных записей. Обратите внимание на макросы DECLARE_FOREIGN_KEY, DECLARE_FOREIGN_KEY_OPT, DECLARE_ARRAY_FOREIGN_KEY и DECLARE_ARRAY_FOREIGN_KEY_OPT, используемые для объявления отношений внешних ключей, которые слишком сложны для BKI_LOOKUP (чаще всего это многоколоночные внешние ключи).

Автоматическое создание типов массивов

Большинство скалярных типов данных должно иметь соответствующий массивный тип (стандартный тип массива varlena, элементом которого является скалярный тип, на который указывает поле typarray в записи pg_type скалярного типа). genbki.pl обычно генерирует запись pg_type для массивного типа автоматически.

Чтобы воспользоваться этой возможностью, просто запишите поле метаданных array_type_oid => nnnn в записи pg_type скалярного типа, указав OID, который следует использовать для типа массива. После этого поле typarray можно не заполнять, поскольку оно будет автоматически заполнено этим OID.

Имя сгенерированного типа массива — это имя скалярного типа с добавлением символа подчеркивания. Остальные поля элемента массива заполняются из аннотации BKI_ARRAY_DEFAULT(value) в pg_type.h, а если таковой нет, то копируются из скалярного типа. (Есть также специальный случай для typalign.) Затем поля typelem и typarray двух записей устанавливаются так, чтобы они ссылались друг на друга.

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

Ниже приведены наиболее простые способы выполнения общих задач при обновлении файлов данных каталога.

Добавьте в каталог новый столбец по умолчанию — добавьте колонку в заголовочный файл с аннотацией 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 в одно поле char:

  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, хотя они будут отображаться только в строках, где они не имеют значений по умолчанию.

  1. Удалите старые столбцы из pg\_proc.h:

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

Примеры кода, производящего массовые модификации, можно найти в скриптах convert_oid2name.pl и remove_pg_type_oid_symbols.pl, вложенных в сообщение.