Объявления и начальное содержимое системных каталогов

QHB использует множество различных системных каталогов для ведения мониторинга за существованием и свойствами объектов базы данных, например, таблиц и функций. Физически системный каталог ничем не отличается от простой пользовательской таблицы, но серверный код на C знает структуру и свойства каждого каталога и может напрямую манипулировать ими на низком уровне. Поэтому, например, не рекомендуется пытаться изменять структуру каталога на лету; это нарушит встроенные в код на C предположения о том, как располагаются строки в каталоге. Тем не менее структура каталогов может меняться от одной основной версии к другой.

Структуры этих каталогов объявляются в специально отформатированных заголовочных файлах C в каталоге /usr/local/qhb/include/catalog/ дерева исходного кода. Для каждого каталога имеется заголовочный файл, названный по имени каталога (например, pg_class.h для pg_class), который определяет набор столбцов в этом каталоге, а также другие основные свойства, например, его OID.

У многих каталогов имеются исходные данные, которые должны быть загружены в них в фазу «начальной загрузки» утилитой qhb_bootstrap (или initdb), чтобы привести систему в состояние, когда она сможет выполнять команды SQL. (Например, файл pg_class.h должен содержать запись для одноименного каталога, а также для всех остальных системных каталогов и индексов.) Эти исходные данные хранятся в редактируемой форме в файлах данных, которых также находятся в каталоге /usr/local/qhb/include/catalog/. Например, в файле pg_proc.dat описываются все исходные строки, которые должны быть добавлены в каталог pg_proc.

Чтобы создать файлы каталогов и загрузить в них эти исходные данные, серверный процесс, работающий в режиме начальной загрузки, прочитывает файл BKI (Backend Interface, серверный интерфейс), содержащий команды и исходные данные. Файл qhb.bki, используемый в этом режиме, создается из вышеупомянутых заголовочных файлов и файлов данных при сборке дистрибутива QHB скриптом на Perl genbki.pl. Хотя файл qhb.bki и привязан к конкретному выпуску QHB, но является платформонезависимым и устанавливается в подкаталог share дерева инсталляции.

Кроме того, скрипт genbki.pl создает для каждого каталога производный заголовочный файл, например, pg_class_d.h для каталога pg_class. Этот файл содержит автоматически генерируемые макроопределения и может содержать другие макросы, объявления перечислений и т. д., которые могут быть полезны для клиентского кода на C, читающего определенный каталог.

Большинству разработчиков QHB нет необходимости заниматься файлом BKI напрямую, но для добавления в код сервера практически любой нетривиальной функциональности потребуется модификация заголовочных файлов и/или файлов исходных данных каталога. Далее в этой главе изложена некоторая информация об этом, а также для полноты описывается формат файла BKI.



Правила объявления системных каталогов

Важнейшей составляющей заголовочного файла каталога является определение структуры на C, описывающее макет каждой строки каталога. Оно начинается с макроса CATALOG, который, когда дело касается компилятора C, является просто сокращенной записью typedef struct FormData_имя_каталога. Каждое поле в этой структуре порождает столбец каталога. Поля можно аннотировать с помощью макросов свойств BKI, описанных в файле genbki.h, например, чтобы определить для поля значение по умолчанию или пометить его как допускающее или не допускающее NULL. Строку CATALOG тоже можно аннотировать некоторыми другими макросами свойств BKI, описанными в genbki.h, чтобы определить другие свойства каталога в целом, например, является ли он разделяемым отношением.

Код кэша системного каталога (и вообще весь код, изменяющий каталог) предполагает, что части всех кортежей системных каталогов, имеющие фиксированную длину, действительно присутствуют, поскольку он отображает на них объявление этой структуры на C. Таким образом, все поля переменной длины и поля, принимающие NULL, должны помещаться в конец, и обращаться к ним как к полям структуры нельзя. К примеру, если вы попытаетесь установить в поле pg_type.typrelid значение NULL, при попытке какого-нибудь куска кода обратиться к полю typetup->typrelid (или, что еще хуже, к полю typetup->typelem, потому что оно следует за typrelid) возникнет сбой. Это приведет к случайным ошибкам или даже нарушениям сегментации.

В качестве частичной защиты от ошибок такого типа поля, имеющие переменную длину или принимающие NULL, нельзя делать видимыми напрямую для компилятора C. Это достигается посредством оборачивания их в #ifdef CATALOG_VARLEN ... #endif (где CATALOG_VARLEN — это символ, который никогда не будет определен). Это не дает коду на C беспрепятственно пытаться обращаться к полям, которые могут отсутствовать или находиться по некоторому другому смещению. В качестве независимой защиты, препятствующей созданию некорректных строк, мы требуем, чтобы все столбцы, которые не должны принимать NULL, помечались соответствующим образом в каталоге pg_attribute. Код начальной загрузки автоматически пометит столбцы каталога как NOT NULL, если они имеют фиксированную ширину и перед ними нет никаких столбцов, принимающих NULL или имеющих переменную ширину. Там, где это правило неприемлемо, вы можете скорректировать пометку, в зависимости от ситуации применив аннотации BKI_FORCE_NOT_NULL и BKI_FORCE_NULL.

Клиентский код не должен включать никакие заголовочные файлы каталогов pg_xxx.h, поскольку эти файлы могут содержать код на C, который не будет компилироваться вне кода сервера. (Обычно это происходит из-за того, что эти файлы также содержат объявления для функций в файлах /usr/local/qhb/ backend/catalog/.) Вместо этого клиентский код может включать соответствующий сгенерированный заголовок pg_xxx_d.h, который будет содержать OID #defines и все остальные данные, которые могут быть полезны на стороне клиента. Если вы желаете, чтобы макрос или другой код в заголовке каталога был видимым для клиентского кода, заключите эту секцию в #ifdef EXPOSE_TO_CLIENT_CODE ... #endif, чтобы указать скрипту genbki.pl скопировать эту секцию в заголовок pg_xxx_d.h.

Некоторые из каталогов настолько основополагающие, что их нельзя даже создать командой BKI create, используемой для большинства каталогов, поскольку этой команде нужно записать в эти каталоги информацию, описывающую новый каталог. Они называются каталогами начальной загрузки, и для их определения требуется много дополнительной работы: вы должны вручную подготовить для них соответствующие записи в предварительно загружаемом содержимом каталогов pg_class и pg_type, и эти записи нужно будет обновлять при последующих изменениях в структуре каталога. (Кроме того, каталогам начальной загрузки необходимы предварительно загруженные записи в каталоге pg_attribute, но, к счастью, сейчас с этой задачей справляется скрипт genbki.pl.) По возможности не делайте из новых каталогов каталоги начальной загрузки.



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

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

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

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

[

# Здесь мог быть комментарий.
{ 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 по заключению в кавычки простых литералов. Обратите внимание, что обратные слэши, фигурирующие в данных, будут обрабатываться сканером начальной загрузки как управляющие символы, в соответствии с теми же правилами, что действуют для управляющих строковых констант (см. подраздел Строковые константы с управляющими последовательностями в стиле C); например, \t преобразуется в символ табуляции. Если вы действительно хотите получить обратный слэш в итоговом значении, вам понадобится написать четыре этих символа: Perl отбрасывает два, оставляя \\ сканеру начальной загрузки.

  • Значения NULL представляются как _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 перед вставкой патчей в данные каталога. Для удобства можно просто переключиться на /usr/local/qhb/include/catalog/ и выполнить make reformat-dat-files.

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


Присвоение OID

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

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

Ввод фактического числового значения любого OID в коде на C считается крайне нежелательным; вместо этого всегда нужно использовать макрос. Прямые обращения к OID в каталоге pg_proc — вполне обычное явление, поэтому существует специальный скрипт Perl Gen_fmgrtab.pl, автоматически создающий необходимые макросы. Имеется также схожий — но по историческим причинам реализованный по-другому — автоматический метод создания макросов для OID в каталоге pg_type. Поэтому в этих двух каталогах записи oid_symbol не нужны. Аналогичным образом устанавливаются макросы для OID системных каталогов и индексов в каталоге pg_class. Для всех остальных каталогов потребуется вручную указать все нужные макросы с помощью записей oid_symbol.

Чтобы найти для новой предварительно загружаемой строки доступный OID, запустите скрипт 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 записи каталога, которой он не присвоен вручную, этот скрипт выберет значение в диапазоне 10000—11999. Счетчик OID сервера получает значение 12000 на старте процесса начальной загрузки. Таким образом, объекты, созданные обычными командами SQL на поздних фазах начальной загрузки, например, объекты, созданные при выполнении скрипта information_schema.sql, получают OID от 12000 и выше.

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


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

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

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

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

  • Для некоторых столбцов каталога в записях допускаются нули вместо корректных ссылок. Если это допустимо, напишите BKI_LOOKUP_OPT вместо BKI_LOOKUP. Затем можно указать 0 в качестве записи столбца (Если столбец объявлен как regproc, можно написать - вместо 0.) За исключением этого особого случая, все записи в столбце BKI_LOOKUP должны быть символическими ссылками. Скрипт genbki.pl предупредит о нераспознанных именах.

  • На большинство видов объектов каталога ссылаются просто по их именам. Обратите внимание, то имена типов должны в точности соответствовать полям typname в записях каталога pg_type; псевдонимы типов использовать нельзя, например, указать integer вместо int4.

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

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

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

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

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

Желательно пометить ссылочные столбцы с 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 (обычно это многостолбцовые внешние ключи).


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

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


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

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

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

    +    /* См. ниже PROKIND_ categories */
    +    char        prokind BKI_DEFAULT(f);
    
  2. Создайте на основе reformat_dat_file.pl новый скрипт для добавления соответствующих значений на лету:

    -           # На данный момент мы имеем в качестве хеша целую строку в памяти
    -           # и можем выполнять любые операции, какие пожелаем. Как уже указывалось
    -           # этот скрипт удаляет только значения по умолчанию, но его можно
    -           # адаптировать для выполнения ситуационного массового редактирования.
    +           # Ситуационное изменение для перемещения в prokind
    +           # К этому моменту значение по умолчанию уже заполнено, поэтому
    +           # при необходимости перейдите к другим значениям
    +           if ($values{proisagg} eq 't')
    +           {
    +               $values{prokind} = 'a';
    +           }
    +           elsif ($values{proiswindow} eq 't')
    +           {
    +               $values{prokind} = 'w';
    +           }
    
  3. Выполните новый скрипт:

    $ cd /usr/local/qhb/include/catalog
    $ perl  rewrite_dat_with_prokind.pl  pg_proc.dat
    

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

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

    -    /* это агрегат? */
    -    bool        proisagg BKI_DEFAULT(f);
    -
    -    /* это оконная функция? */
    -    bool        proiswindow BKI_DEFAULT(f);
    
  5. Наконец, выполните make reformat-dat-files, чтобы удалить бесполезные старые записи из файла pg_proc.dat.



Формат файла BKI

В этом разделе описывается, как сервер QHB интерпретирует файлы BKI. Это описание будет легче понять, если под рукой в качестве примера будет файл qhb.bki.

Вводимая в BKI информация состоит из последовательности команд. Команды формируются из нескольких синтаксических единиц, в зависимости от синтаксиса конкретной команды. Синтаксические единицы обычно разделяются пробельными символами, но это необязательно, если нет какой-то неоднозначности. Специальный разделитель команд отсутствует; следующая синтаксическая единица, которая синтаксически не может принадлежать предыдущей команде, начинает новую команду. (Обычно для ясности новую команду размещают в новой строке.) Синтаксическими единицами могут быть определенные ключевые слова, специальные символы (скобки, запятые и т. п.), идентификаторы, числа или строки в апострофах. Все они чувствительны к регистру.

Строки, начинающиеся с #, игнорируются.



Команды BKI

create имя_таблицы oid_таблицы [bootstrap] [shared_relation] [rowtype_oid oid] (имя1 = тип1 [FORCE NOT NULL | FORCE NULL ] [, имя2 = тип2 [FORCE NOT NULL | FORCE NULL ], ...])

Создать таблицу имя_таблицы с заданным oid_таблицы и столбцами, указанными в скобках.
Напрямую bootstrap.c поддерживает следующие типы столбцов: bool, bytea, char (1 байт), name, int2, int4, regproc, regclass, regtype, text, oid, tid, xid, cid, int2vector, oidvector, _int4 (массив), _text (массив), _oid (массив), _char (массив), _aclitem (массив). Хотя возможно создавать таблицы, содержащие столбцы других типов, это нельзя сделать, пока не будет создан и заполнен соответствующими записями каталог pg_type. (По сути это означает, что в каталогах начальной загрузки могут использоваться только эти типы столбцов, но другие каталоги могут содержать любые встроенные типы.)
С указанием bootstrap таблица будет создана только на диске; никакие записи о ней не будут введены в каталоги pg_class, pg_attribute и т. д. Таким образом, таблица не будет доступна для обычных операций SQL, пока такие записи не будут созданы специально (командами insert). Этот параметр используется для создания самих каталогов pg_class и им подобных.
Если указать shared_relation, таблица создается как разделяемая. Дополнительно посредством предложения rowtype_oid можно задать OID типа строки таблицы (OID записи в pg_type); если оно не указано, OID генерируется автоматически. (Предложение rowtype_oid бесполезно, если указано bootstrap, но его все равно можно добавить для документирования.)

open имя_таблицы

Открыть таблицу имя_таблицы для добавления данных. Любая другая таблица, открытая в данный момент, закрывается.

close имя_таблицы

Закрыть открытую таблицу. Имя таблицы должно задаваться для перепроверки.

insert ( [значение_oid] значение1 значение2 ... )

Добавить новую строку в открытую таблицу, используя в качестве значений столбцов значение1, значение2, и т. д.
Значения NULL можно задать с помощью специального ключевого слова _null_. Значения, не похожие на идентификаторы или цифровые строки, должны заключаться в апострофы. (Чтобы включить апостроф в значение, продублируйте его. Также в строке разрешено экранирование в стиле управляющей строки обратного слэша.)

declare [unique] index имя_индекса oid_индекса on имя_таблицы using имя_метода_доступа ( класс_оп1 имя1 [, ...] )

Создать индекс имя_индекса с заданным oid_индекса в таблице имя_таблицы с методом доступа имя_метода_доступа. Индекс строится по полям имя1, имя2 и т. д., и для них используются классы операторов класс_оп1, класс_оп2 и т. д. соответственно. Эта команда создает файл индекса и добавляет для него соответствующие записи в каталог, но не инициализирует содержимое индекса.

declare toast oid_таблицы_toast oid_индекса_toast on имя_таблицы

Создать таблицу TOAST для таблицы имя_таблицы. Таблице TOAST присваивается заданный oid_таблицы_toast, а ее индексу — oid_индекса_toast. Как и с declare index, заполнение индекса откладывается.

build indices

Заполнить индексы, объявленные ранее.



Структура файла BKI начальной загрузки

Команду open нельзя применять, если используемые ей таблицы не существуют и в них нет записей для открываемой таблицы. (Минимальный набор этих таблиц: pg_class, pg_attribute, pg_proc и pg_type.) Чтобы можно было заполнить сами эти таблицы, команда create с параметром bootstrap неявно открывает создаваемую таблицу для добавления данных.

Кроме того, команды declare index и declare toast нельзя применять, пока не будут созданы и заполнены нужные им системные каталоги.

Таким образом, структура файла qhb.bki должна быть следующей:

  1. create bootstrap (создать) одну из критичных таблиц.

  2. insert (добавить) данные, описывающие как минимум критичные таблицы.

  3. close (закрыть).

  4. Повторить для других критичных таблиц.

  5. create (создать) (без bootstrap) некритичную таблицу.

  6. open (открыть).

  7. insert (добавить) желаемые данные.

  8. close (закрыть).

  9. Повторить для других некритичных таблиц.

  10. Определить индексы и таблицы TOAST.

  11. build indices (заполнить индексы).

Несомненно, существуют и другие, недокументированные зависимости, определяющие конкретный порядок.



Пример BKI

Следующая последовательность команд создаст таблицу test_table с OID 420, имеющую три столбца oid, cola и colb типов oid, int4 и text соответственно, и добавит две строки в эту таблицу:

create test_table 420 (oid = oid, cola = int4, colb = text)
open test_table
insert ( 421 1 'value 1' )
insert ( 422 2 _null_ )
close test_table