カタログヘッダーファイルの肝心な部分は、カタログにおける行の配置を記述するC構造体定義です。
これはCATALOGマクロで始まりますが、Cコンパイラの観点からすると、単にtypedef struct
FormData_の短縮形です。
構造体の各々のフィールドは、カタログのカラムを生成します。
フィールドにはcatalognamegenbki.hに記述されたBKIプロパティマクロを使って注釈を付けることができます。
たとえば、フィールドのデフォルト値を定義したり、NULLが許されるかどうかのフラグを付けることができます。
CATALOG行にも注釈が付けられます。
genbki.hに記述されたBKIプロパティマクロを使って、共有リレーションであるかどうかといった、そのカタログ全体のプロパティを定義することができます。
システムカタログキャッシュのコード(そして一般的にたいていのカタログを触るコード)は、すべてのシステムカタログタプルに固定長部分が実際に存在するとみなします。
システムカタログキャッシュのコードは、C構造体定義をその固定部分にマップするからです。
したがって、すべての可変長フィールドと、NULLを許容するフィールドは、最後尾に置かれなければならず、また、構造体のフィールドとしてはアクセスできません。
たとえば、pg_type.typrelidをNULLにしようとすると、他のコード部分がtypetup->typrelidを参照しようとして失敗します。(あるいはもっと悪いことにtypetup->typelemを参照中に失敗します。なぜなら、そのフィールドはtyprelidの後に来るからです。)
これはランダムなエラーとなるか、あるいはセグメンテーション違反にすらなってしまいます。
この種のエラーから部分的に身を守るためには、可変長あるいはNULLを許容するフィールドはCコンパイラから直接見えないようにすべきです。
これは#ifdef CATALOG_VARLEN ... #endifの中に入れることで達成できます。(ここで、CATALOG_VARLENは、決して定義されないシンボルです。)
これにより、Cコードが不注意で存在しないフィールドにアクセスしようとしたり、オフセットが違うフィールドにアクセスしようとするのを防ぐことができます。
不正な行を作るのを防ぐ独立したガードとして、NULLを許容しないすべてのカラムをpg_attributeでそのように宣言することを要求します。
ブートストラップコードは、固定長でかつNULLを許容するカラムの次ではないすべてのカラムに対して自動的にNOT NULLのマークを付けます。
このルールが不適切なら、BKI_FORCE_NOT_NULLとBKI_FORCE_NULLを必要に応じて使ってマーキングを修正できます。
フロントエンドのコードはすべてのpg_xxx.hカタログヘッダーファイルをincludeすべきではありません。
バックエンド以外ではコンパイルできないCコードを含んでいるかもしれないからです。
(典型的には、src/backend/catalog/ファイル中に関数宣言を含んでいることによって起こります。)
その代わりに、フロントエンドは生成されたpg_xxx_d.hヘッダーをincludeできます。
このファイルは、OIDの#defineと、クライアント側で必要になるデータを含んでいます。
カタログヘッダー中のマクロやその他のコードをフロントエンドから見えるようにしたい場合は、#ifdef EXPOSE_TO_CLIENT_CODE ... #endifで該当セクションを囲むことにより、genbki.plがそのセクションをpg_xxx_d.hにコピーするように指示してください。
少数のカタログは非常に基本的なものなので、ほとんどのカタログで使用されるBKI createコマンドですら作成できません。そのコマンドが、新しいカタログの記述をこれらのカタログに書き込む必要があるからです。
これらはブートストラップカタログと呼ばれ、定義するためには大量の追加の作業が必要です。
pg_classとpg_typeのあらかじめロード済みの内容上に手動で適切なエントリを用意し、後のカタログ構造への変更に合わせてそれらのエントリを更新する必要があります。
(また、ブートストラップカタログはpg_attribute中のロード済みのエントリを必要としますが、幸いにも最近はgenbki.plが適切に処理してくれます。)
可能ならば、新しいカタログをブートストラップカタログとして作るのは避けてください。