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

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

Код кеша системного каталога (и в принципе почти весь код, манипулирующий каталогом) предполагает, что имеющие постоянный размер части всех кортежей системных каталогов присутствуют фактически, так как он отображает на них объявления структуры на 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. Но заметьте, что ограничения NOT NULL контролируются только исполнителем запросов; на уровне кода C они не действуют, поэтому создавать или изменять строки каталога вручную нужно так же аккуратно.

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

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