72.2. Начальные данные системного каталога#

72.2. Начальные данные системного каталога

72.2. Начальные данные системного каталога #

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

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

Каждый файл .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_' },

]

Важные моменты:

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

  • В каждой строке каталога записываются пары key => value, разделенные запятыми. Разрешенные key - это имена столбцов каталога, а также ключи метаданных oid, oid_symbol, array_type_oid и descr. (Использование oid и oid_symbol описано ниже в разделе Раздел 72.2.2, а array_type_oid описан в разделе Раздел 72.2.4. descr предоставляет строку описания объекта, которая будет вставлена в pg_description или pg_shdescription при необходимости). Хотя ключи метаданных являются необязательными, все определенные столбцы каталога должны быть указаны, за исключением случаев, когда файл .h каталога указывает значение по умолчанию для столбца. (В приведенном выше примере поле datdba было не указано, потому что файл pg_database.h предоставляет подходящее значение по умолчанию для него).

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

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

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

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

  • Поскольку хеши являются неупорядоченными структурами данных, порядок полей и расположение строк не имеют семантического значения. Однако, чтобы сохранить единообразный вид, мы устанавливаем несколько правил, которые применяются сценарием форматирования 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() расширять данные обратно в полное представление.

72.2.2. Назначение 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. Аналогично, макросы для OID pg_class системных каталогов и индексов настраиваются автоматически. Для всех остальных системных каталогов необходимо вручную указать любые необходимые макросы с помощью записей 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 не считаются стабильными и могут изменяться от одной установки к другой.

72.2.3. Поиск ссылки на OID #

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

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

  • Также допускается присоединение BKI_LOOKUP(encoding) к целочисленным столбцам для ссылки на кодировки набора символов, которые в настоящее время не представлены в виде идентификаторов каталога, но имеют набор значений, известных 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(argtypename,argtypename,...), как regprocedure. Имена аргументов типа должны быть написаны точно так, как они указаны в поле proargtypes записи pg_proc.dat. Не вставляйте пробелы.

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

  • Имена opclass и opfamily уникальны только в рамках метода доступа, поэтому они представлены в виде access_method_name/object_name.

  • В ни одном из этих случаев не предусмотрено использование схемы; все объекты, созданные во время загрузки, ожидается, что будут находиться в схеме 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 (обычно, многоколоночные внешние ключи).

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

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

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

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

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

Добавление значения по умолчанию для существующей колонки, которая не имеет значения по умолчанию:  Добавьте аннотацию 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