75.1. Правила объявления системных каталогов #
Ключевой частью заголовочного файла каталога является описание структуры на 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, который не будет компилироваться вне кода сервера. (Обычно это происходит из-за того, что эти файлы также содержат объявления функций в файлах 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
.) По возможности избегайте включения новых каталогов в категорию каталогов начальной загрузки.
75.1. System Catalog Declaration Rules #
The key part of a catalog header file is a C structure definition describing the layout of each row of the catalog. This begins with a CATALOG
macro, which so far as the C compiler is concerned is just shorthand for typedef struct FormData_
. Each field in the struct gives rise to a catalog column. Fields can be annotated using the BKI property macros described in catalogname
genbki.h
, for example to define a default value for a field or mark it as nullable or not nullable. The CATALOG
line can also be annotated, with some other BKI property macros described in genbki.h
, to define other properties of the catalog as a whole, such as whether it is a shared relation.
The system catalog cache code (and most catalog-munging code in general) assumes that the fixed-length portions of all system catalog tuples are in fact present, because it maps this C struct declaration onto them. Thus, all variable-length fields and nullable fields must be placed at the end, and they cannot be accessed as struct fields. For example, if you tried to set pg_type
.typrelid
to be NULL, it would fail when some piece of code tried to reference typetup->typrelid
(or worse, typetup->typelem
, because that follows typrelid
). This would result in random errors or even segmentation violations.
As a partial guard against this type of error, variable-length or nullable fields should not be made directly visible to the C compiler. This is accomplished by wrapping them in #ifdef CATALOG_VARLEN
... #endif
(where CATALOG_VARLEN
is a symbol that is never defined). This prevents C code from carelessly trying to access fields that might not be there or might be at some other offset. As an independent guard against creating incorrect rows, we require all columns that should be non-nullable to be marked so in pg_attribute
. The bootstrap code will automatically mark catalog columns as NOT NULL
if they are fixed-width and are not preceded by any nullable or variable-width column. Where this rule is inadequate, you can force correct marking by using BKI_FORCE_NOT_NULL
and BKI_FORCE_NULL
annotations as needed.
Frontend code should not include any pg_xxx.h
catalog header file, as these files may contain C code that won't compile outside the backend. (Typically, that happens because these files also contain declarations for functions in src/backend/catalog/
files.) Instead, frontend code may include the corresponding generated pg_xxx_d.h
header, which will contain OID #define
s and any other data that might be of use on the client side. If you want macros or other code in a catalog header to be visible to frontend code, write #ifdef EXPOSE_TO_CLIENT_CODE
... #endif
around that section to instruct genbki.pl
to copy that section to the pg_xxx_d.h
header.
A few of the catalogs are so fundamental that they can't even be created by the BKI create
command that's used for most catalogs, because that command needs to write information into these catalogs to describe the new catalog. These are called bootstrap catalogs, and defining one takes a lot of extra work: you have to manually prepare appropriate entries for them in the pre-loaded contents of pg_class
and pg_type
, and those entries will need to be updated for subsequent changes to the catalog's structure. (Bootstrap catalogs also need pre-loaded entries in pg_attribute
, but fortunately genbki.pl
handles that chore nowadays.) Avoid making new catalogs be bootstrap catalogs if at all possible.