Правила декларирования системного каталога
Ключевой частью заголовочного файла каталога является описание структуры на C, определяющее вид каждой строки каталога. Оно начинается с макроса CATALOG
, который, если говорить о компиляторе C, является просто сокращенной записью typedef struct FormData_имя_каталога
. Каждое поле в этой структуре порождает столбец каталога. Поля можно дополнить макросами свойств BKI, объявленными в genbki.h
. Например, для поля можно задать значение по умолчанию или указать, допускается ли в нем NULL. Строку CATALOG
можно также дополнить некоторыми другими макросами свойств BKI, объявленными в genbki.h
и определяющими другие свойства каталога в целом, например, является ли он общим.
Код кеша системного каталога (и практически весь код, работающий с каталогом) основывается на том, что части всех кортежей системных каталогов с постоянным размером реально существуют, так как они отображены на структуры языка C. Поля переменной длины и допускающие значение NULL должны находиться в конце, и доступ к ним через структуру невозможен. Например, если присвоить полю pg_type.typrelid
значение NULL, попытка обращения где-либо в коде к typetup->typrelid
(или, что еще хуже, к следующему за ним полю typetup->typelem
) приведет к ошибке. Это может вызвать непредсказуемые сбои или даже нарушение сегментации.
В качестве частичной защиты от ошибок такого типа поля переменной длины или поля, принимающие 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, который не будет компилироваться вне кода сервера. (Обычно это происходит из-за того, что эти файлы также содержат объявления функций в файлах 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
.) По возможности избегайте включения новых каталогов в категорию каталогов начальной загрузки.
Ключевой частью заголовка каталога является определение структуры на C, описывающее компоновку каждой строки каталога. Это начинается с макроса CATALOG
, который, насколько известно компилятору на C, является всего лишь сокращением имени структуры Typedef
FormData_catalogname
. Каждое поле в структуре порождает столбец каталога. Поля могут быть аннотированы с помощью макросов свойств BKI, описанных в genbki.h
, например, чтобы определить значение по умолчанию для поля или пометить его как нулевое или ненулевое. Строка CATALOG
также может быть аннотирована некоторыми другими макросами свойств BKI, описанными в genbki.h
, чтобы определить другие свойства каталога в целом, например, является ли он общим отношением.
Код кеша системного каталога (и большая часть кода, работающего на основе каталога в целом) предполагает, что части всех кортежей системного каталога с фиксированной длиной действительно присутствуют, так как они отображаются на них. Это объявление структуры C. Таким образом, все поля переменной длины и поля, которые могут быть аннулированы, должны быть размещены в конце, и к ним нельзя получить доступ как к полям структуры. Например, если установить pg_type.typrelid
как NULL, это потерпит неудачу, если какой-либо фрагмент кода попытается сослаться на typetup\-\>typrelid
(или хуже, typetup->typelem
, потому что это следует за typrelid
). Это приведет к случайным ошибкам или даже нарушениям сегментации