Перейти к основному содержимому

Правила декларирования системного каталога

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