70.2. Исходные данные системных каталогов
Для каждого каталога, с которым связаны вручную создаваемые исходные данные, (не все каталоги такие) имеется соответствующий файл .dat
, содержащий эти данные в редактируемом формате.
70.2.1. Формат файла данных
Каждый файл .dat
содержит описания структур данных Perl, в результате вычисления которых (функцией eval) в памяти формируется структура данных, состоящая из массива хеш-ссылок, соответствующих каждой строке каталога. Немного модифицированная выдержка из pg_database.dat
иллюстрирует основные моменты:
[ # Здесь мог быть комментарий. { oid => '1', oid_symbol => 'TemplateDbOid', descr => 'database\'s default template', datname => 'template1', encoding => 'ENCODING', datcollate => 'LC_COLLATE', datctype => 'LC_CTYPE', datistemplate => 't', datallowconn => 't', datconnlimit => '-1', datlastsysoid => '0', datfrozenxid => '0', datminmxid => '1', dattablespace => 'pg_default', datacl => '_null_' }, ]
Замечания:
Общий формат файла: открывающая квадратная скобка, один или более наборов фигурных скобок, каждый из которых представляет строку каталога, и закрывающая квадратная скобка. После каждой закрывающей фигурной скобки должна идти запятая.
В каждой строке каталога записываются разделённые запятыми пары
ключ
=>
значение
. В качествеключа
принимаются имена столбцов каталога, а также ключи метаданныхoid
,array_type_oid
,oid_symbol
иdescr
. (Использованиеoid
иoid_symbol
описывается в Подразделе 70.2.2, аarray_type_oid
— в Подразделе 70.2.4. Вdescr
задаётся строка с описанием объекта, которое будет вставлено вpg_description
илиpg_shdescription
.) Ключи метаданных могут опускаться, но ключ для каждого столбца каталога должен присутствовать, если только в файле.h
данного каталога для столбца не задано значение по умолчанию. (Для иллюстрации в показанном выше примере опущено полеdatdba
, так как вpg_database.h
для него задаётся подходящее значение по умолчанию.)Все значения должны заключаться в апострофы. Апострофы внутри значений экранируются обратной косой чертой. Обратные косые черты в данных могут, но не обязательно должны дублироваться; это соответствует правилам Perl по оформлению простых строковых констант. Заметьте, что обратные косые черты, фигурирующие в данных, будут обрабатываться сканером исходных данных как символы экранирования, согласно тем же правилам записи строковых констант (см. Подраздел 4.1.2.2); например,
\t
преобразуется в символ табуляции. Если вы хотите получить именно обратную косую черту в окончательном значении, вам надо будет написать четыре этих символа: Perl отбрасывает два и оставляет\\
сканеру исходных данных.Значения NULL представляются как
_null_
. (Заметьте, что создать значение с именно такой строкой невозможно.)Комментарии предваряются знаком
#
и должны размещаться в отдельных строках.Значения полей, выражающие OID других записей каталога, должны представляться символьными именами, а не числовыми кодами OID. (В примере выше такое символьное значение задаётся для поля
dattablespace
.) Об этом рассказывается в Подразделе 70.2.3.Так как хеши являются неупорядоченной структурой данных, порядок полей и расположение строк не имеют семантической значимости. Однако для поддержания согласованного представления мы установили несколько правил, которые применяет скрипт форматирования
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
рекомендуется запускать перед сохранением изменений в данных каталога. Им удобно пользоваться, просто выполняяmake reformat-dat-files
вsrc/include/catalog/
.Если вы хотите добавить новый метод уменьшения представления данных, вы должны реализовать его в
reformat_dat_file.pl
и также научитьCatalog::ParseData()
разворачивать данные в полное представление.
70.2.2. Назначение OID
Строке каталога, фигурирующей в исходных данных, можно вручную присвоить OID, добавив поле метаданных oid =>
. Более того, когда строке присваивается OID, для этого OID можно создать макрос C, добавив поле метаданных nnnn
oid_symbol =>
.имя
Предварительно загружаемым строкам каталога должны заранее назначаться 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, вносимых модификациями, следует, что выбранные в них изначально значения OID не должны считаться стабильными, пока эта модификация не будет включена в официальный выпуск. После выпуска мы не будем менять вручную назначенные OID объектов, так как это может повлечь самые разные проблемы совместимости.
Если скрипту genbki.pl
требуется назначить OID элементу каталога, для которого OID не назначен вручную, он выбирает значение в диапазоне 10000—11999. Счётчик OID сервера получает значение 12000 на стадии начальной инициализации. Таким образом, объекты, создаваемые обычными командами SQL на поздних стадиях инициализации, например объекты, создаваемые скриптом information_schema.sql
получат значения OID от 12000 и выше.
В процессе обычной работы сервер назначает OID от 16384 и выше. Тем самым гарантируется, что диапазон 10000—16383 свободен для значений OID, назначаемых автоматически скриптом genbki.pl
или при инициализации кластера. Эти автоматически назначаемые OID не считаются стабильными и могут меняться от одного кластера к другому.
70.2.3. Поиск OID по ссылке
Вообще говоря, из одной строки исходного каталога можно сослаться на другую, просто указав предопределённый OID целевой строки в поле ссылки. Однако политика проекта не приветствует такой подход, так как он провоцирует ошибки, а подобные ссылки сложны для восприятия и могут нарушиться при перенумерации OID. Поэтому в genbki.pl
реализованы механизмы записи символических ссылок, работающие по следующим правилам:
Для использования символических ссылок в некотором столбце каталога требуется добавить указание
BKI_LOOKUP(
в определение этого столбца, гдеправило_поиска
)правило_поиска
— имя целевого каталога, напримерpg_proc
. УказаниеBKI_LOOKUP
может быть добавлено к столбцам типаOid
,regproc
,oidvector
илиOid[]
; в последних двух случаях поиск будет выполняться для каждого элемента массива.Также допускается указание
BKI_LOOKUP(encoding)
для целочисленных столбцов, позволяющее обращаться к кодировкам символов (в настоящее время кодировкам не соответствуют OID в каталоге, но скриптуgenbki.pl
известен их набор).В таком столбце все записи должны иметь символьный формат (исключение составляет
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
.В дополнение к общим механизмам поиска есть также специальное соглашение, согласно которому обозначение
PGNSP
заменяется на OID схемыpg_catalog
, аPGUID
— на OID роли суперпользователя, выполняющего начальную инициализацию. Специальное использование этих обозначений объясняется историческими причинами, но унифицировать их пока не было необходимости.
Скрипт genbki.pl
разрешает все символические ссылки при запуске и помещает в формируемый файл BKI обычные числовые OID. Таким образом, при начальной загрузке отпадает необходимость в разрешении имён.
70.2.4. Автоматическое создание типов массивов
Для большинства скалярных типов данных должны создаваться соответствующие типы массивов (то есть стандартный тип массива переменной длины с элементами скалярного типа, ссылка на который содержится в поле typarray
скалярного типа pg_type
). Как правило, скрипт genbki.pl
может создать запись в pg_type
для типа массива автоматически.
Для использования этой возможности достаточно записать array_type_oid =>
в поле метаданных записи nnnn
pg_type
для скалярного типа, указав OID, который будет использоваться для типа массива. После этого поле typarray
можно не задавать, так как этот OID окажется в нём автоматически.
В качестве имени сгенерированного типа массива выбирается имя скалярного типа с добавленным спереди подчёркиванием. Другие поля в записи массива заполняются из определений BKI_ARRAY_DEFAULT(
в значение
)pg_type.h
или, если соответствующего определения нет, копируются из скалярного типа. (Поле typalign
заполняется особым образом.) Затем в полях typelem
и typarray
двух записей устанавливаются перекрёстные ссылки друг на друга.
70.2.5. Рецепты по редактированию файлов данных
Ниже приведены некоторые предложения по оптимальному решению некоторых распространённых задач при изменении файлов каталогов.
Добавление в каталог нового столбца со значением по умолчанию: Добавьте столбец в заголовочный файл с указанием 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
в символьном поле:
Добавьте новый столбец со значением по умолчанию в
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
окажутся все три столбца,prokind
,proisagg
иproiswindow
, хотя они будут фигурировать только в тех строках, где им присваиваются не значения по умолчанию, а любые другие значения.Удалите старые столбцы из
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-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