手動で生成した初期データを持つ(いくつかのものは持っていません)各々のカタログには、編集可能なデータ形式の初期データを含み、対応する.dat
ファイルがあります。
個々の.dat
ファイルにはPerlのデータ構造文字列が含まれます。
それらは単に評価されることによって1個がカタログの1行に対応するハッシュ参照の配列を含むメモリ上のデータ構造を生成します。
pg_database.dat
から抜きだしたものに些細な変更を加えたものを使って、鍵となる機能を示します。
[ # A comment could appear here. { oid => '1', oid_symbol => 'TemplateDbOid', descr => 'database\'s default template', datname => 'template1', encoding => 'ENCODING', datcollate => 'LC_COLLATE', datctype => 'LC_CTYPE', datistemplate => 't', datallowconn => 't', datconnlimit => '-1', datlastsysoid => '0', datfrozenxid => '0', datminmxid => '1', dattablespace => 'pg_default', datacl => '_null_' }, ]
特筆すべきポイント:
全体的なファイルレイアウトは次のようになります。 開き大括弧、カタログの行を表現する一つ以上の中括弧、閉じ大括弧。 各々の閉じ中括弧の後にはカンマを書きます。
各々のカタログ行にカンマ区切りでkey
=>
value
ペアを書きます。
記述可能なkey
は、カタログのカラムに加えてメタデータキーであるoid
、oid_symbol
、array_type_oid
、descr
です。
(oid
とoid_symbol
の使い方は後述の69.2.2で説明されていて、一方、array_type_oid
は69.2.4で説明されています。
descr
はオブジェクトの説明文字列に使用し、pg_description
かpg_shdescription
の適切な方に挿入されます。)
メタデータキーは省略可能であるのに対し、カタログの.h
ファイルがカラムのデフォルト値を指定する場合を除いてカタログの定義済みカラムはすべて提供されなければなりません。
(上記の例ではpg_database.h
が適切なデフォルト値を供するのでdatdba
フィールドは省略されました。)
すべての値は単一引用符で囲まなければなりません。
値中の単一引用符はバックスラッシュでエスケープします。
データを意味するバックスラッシュは二重にできますが、必須ではありません。
これはPerlの単純な単一引用符で引用されたリテラルに関するルールに基づいています。
データとして使われるバックスラッシュは、エスケープ文字列定数(4.1.2.2参照)と同じルールに基づき、ブートストラップスキャナーはエスケープと解釈することに注意してください。
たとえば\t
はタブへと変換されます。
最終的な値としてバックスラッシュを使用したい場合は、4つ書く必要があります。
Perlが2つ削除し、ブートストラップスキャナーが認識するために\\
が残ります。
NULL値は_null_
で表します。
(それと同じ文字列を作る方法はないことに注意してください。)
コメントは#
を前に置いてください。また同じ行上に置かなければなりません。
他のカタログエントリのOIDであるフィールド値は、実際の数値のOIDではなくシンボル名で記述されるべきです。
(上記の例ではdattablespace
がこのような参照を含みます。)
これは後述の69.2.3で説明します。
ハッシュは順序付けられないデータ構造なので、フィールドの順や行の配置には重要な意味はありません。
しかし、見た目を統一するために、フォーマットスクリプトreformat_dat_file.pl
が適用される少数のルールを設定しました。
中括弧のペアの中で、メタデータフィールドのoid
、oid_symbol
、array_type_oid
、および、(もし存在するなら)descr
がこの順で最初に来ます。
そして、定義された順にカタログ自身のフィールドが現れます。
可能ならば、行の長さを80文字に制限するために、必要に応じてフィールドの間に改行を挿入します。 改行はメタデータフィールドと通常のフィールドの間にも挿入します。
カタログの.h
ファイルがカラムのデフォルト値を指定していて、データエントリが同じ値なら、reformat_dat_file.pl
はデータファイルからデータエントリを省略します。
これでデータ表現が小さくなります。
reformat_dat_file.pl
は空白行とコメント行をそのまま維持します。
カタログデータパッチを投稿する前に、reformat_dat_file.pl
を実行することをお勧めします。
便利さのために、単にsrc/include/catalog/
に変更を加えてmake reformat-dat-files
を実行することができます。
データ表現をより小さくする新しい方法を付け加えたいのであれば、reformat_dat_file.pl
で実装し、また
データを完全な表現に戻す方法をCatalog::ParseData()
に指示しなければなりません。
初期データに現れるカタログ行にはoid=>
メタデータフィールドを書くことで手動で割り当てたOIDを与えることができます。
それだけでなく、OIDを割り当てられたならば、nnnn
oid_symbol =>
メタデータフィールドを書くことでそのOID用のCマクロを作ることができます。
name
他のプリロードカタログ行の中にそのOIDへの参照がある場合には、プリロードカタログ行は割当済みのOIDを持たなければなりません。
Cコードから行OIDが参照されるときにも割当済みのOIDは必要です。
どちらも当てはまらない場合は、oid
メタデータフィールドは省略可能です。
その場合、ブートストラップコードが自動的にOIDを割り当てます。
実用的には、カタログの一部のみが実際に相互参照されている場合でも、与えられたプリロードカタログ行のOIDをすべて割当済みにするか、一つも割当済みにしないかのどちらかに通常します。
Cコード中でOIDの実際の数値を書くのは非常に良くないと考えられます。
pg_proc
を直接参照するのは普通のことなので、自動的に必要なマクロを生成する特別な仕掛けがあります。
src/backend/utils/Gen_fmgrtab.pl
を見てください。
歴史的理由により、似ていはいますが同じではない方法によるpg_type
OID用のマクロを自動生成する仕組みがあります。
ですから、oid_symbol
エントリはこれらの2つのカタログに必ずしも存在しなければならないというわけではありません。
同様に、pg_class
システムカタログのOIDとインデックスマクロは自動的に設定されます。
他のすべてのシステムカタログでは、oid_symbol
を使って必要なマクロを手動で指定しなければなりません。
新しいプリロード行のために利用可能なOIDを見つけるには、src/include/catalog/unused_oids
スクリプトを実行してください。
未使用のOIDの範囲が表示されます。
(たとえば、「45-900」はOIDs 45から900が利用されていないことを示します。)
今の所OID 1-9999 は手動での割当のために予約されています。
unused_oids
スクリプトは、単にカタログヘッダーと.dat
を見てそこに出現していないOIDを探しているだけです。
間違い見つけるためにduplicate_oids
を利用することもできます。
(genbki.pl
は手動アサインされていない全ての行にOIDを割り当て、また、コンパイル時に重複OIDを検出します。)
即座にコミットされるとは期待できないパッチ用にOIDを選ぶときの最良の手法は、8000—9999の範囲でランダムに選択したところから始まるおおむね連続したOIDのグループを使うことです。
これは同時に開発されている他のパッチとのOID衝突の危険を最小化します。
8000—9999の範囲を開発目的に空けておくため、パッチがマスタgitリポジトリにコミットされた後、そのOIDはこの範囲の下位の使用可能な場所に番号の振り直しをすべきです。
通例これは各開発サイクルの終わり近くに行われ、同時にそのサイクルでコミットされたパッチで消費された全てのOIDを移動するでしょう。
スクリプトrenumber_oids.pl
はこの目的に使用できます。
コミットされていないパッチが最近コミットされたパッチとOID衝突していることに気づいた場合に、このような状況から回復するのにもrenumber_oids.pl
がおそらく役立つでしょう。
パッチに割り当てられたOIDを番号付け替えすることがあるこの慣習のため、パッチに割り当てられたOIDはそのパッチが正式リリースに含まれるまでは永続的と考えるべきではありません。 さまざまな互換性の問題を生み出すかもしれないため、一度リリースされた手動でアサインされたオブジェクトのOIDは変更しません。
genbki.pl
は、手動アサインされたOIDを持たないカタログエントリにOIDを割り当てる必要がある場合、10000—11999範囲の値を使います。
ブートストラップ実行開始の際にはサーバのOIDカウンタは12000に設定されます。
したがって、information_schema.sql
スクリプトを実行して作られるオブジェクトなど、ブートストラップ後の段階において通常のSQLコマンドで作られたオブジェクトは12000以上のOIDを受け取ります。
通常のデータベース操作で割り当てられたOIDは16384以上に限定されます。
これはgenbki.pl
やブートストラップの中で自動的に割り当てられたOIDに対して10000—16383の範囲が空いていること保証します。
これらの自動割り当てされたOIDは永続的とはいえず、あるインストレーションから他のインストレーションで変更されるかもしれません。
原則としては、ある初期カタログ行から他への相互参照は、参照しているフィールドで参照されている行の事前割り当てされたOIDを書くことだけで記述できます。
しかしながら、間違いやすく、読みにくく、また、新たに割り当てられたOIDが番号付け直しされたときに破損しやすいため、これはプロジェクト方針に反します。
そのため、genbki.pl
が代わりにシンボル参照を記述する機構を提供しています。
そのルールは以下のとおりです。
BKI_LOOKUP(
をカラム定義に加えることで、特定のカタログカラムでのシンボル参照が利用可能になります。
ここでlookuprule
)lookuprule
は参照されているカタログ名で、例えばpg_proc
です。
BKI_LOOKUP
を、Oid
、regproc
、oidvector
、Oid[]
のカラムに加えることができます。
最後の2つにおいては、配列の個々の要素を検索することを暗に意味します。
文字セット符号化方式を参照する整数の列にBKI_LOOKUP(encoding)
を加えることも許容されます。これは今のところカタログのOIDとして現れませんが、値の集合をgenbki.pl
に知らせます。
そうしたカラムでは、InvalidOidの意味で0
を用いる以外には、すべてのエントリでシンボル参照を使用しなければなりません。
(カラムがregproc
と宣言されている場合は、0
の代わりに-
と書くことができます。)
genbki.pl
は認識できない名前には警告を出します。
たいていのカタログオブジェクト類は単純にその名前で参照されます。
型名は参照されているpg_type
のエントリのtypname
と正確に一致しなければならないことに注意してください。int4
に対するinteger
などの別名は使えません。
それがpg_proc.dat
内でユニークなら、関数はproname
で表現できます。
(regprocの入力はこのように働きます。)
そうでなければ、regprocedureのように、proname(argtypename,argtypename,...)
と書いてください。
引数型名は正確にpg_proc.dat
エントリのproargtypes
で指定しなければなりません。
空白は挿入しないでください。
演算子はoprname(lefttype,righttype)
で表現します。
型名は正確にpg_operator.dat
エントリのoprleft
フィールドとoprright
フィールドで記述します。
(省略された単項演算子のオペランドは0
と書きます。)
opclassesとopfamiliesの名前はアクセスメソッド内でのみユニークなので、access_method_name
/
object_name
で表します。
以上のいずれの場合にもスキーマ修飾の規定はありません。
ブートストラップ中に作成されるすべてのオブジェクトは、pg_catalog
スキーマにあると期待されます。
一般的な検索の仕組みに加えて、PGNSP
がpg_catalog
スキーマのOIDで置換され、PGUID
がブートストラップのスーパーユーザロールのOIDで置換されるという特別な習慣があります。
これらの使用方法はやや歴史的なものですが今のところ、これらを一般化する必要はありませんでした。
genbki.pl
は実行中にすべてのシンボル参照を解決し、生成したBKIファイルの中に単純な数字のOIDを設定します。
ですから、ブートストラップバックエンドはシンボル参照にかかわる必要はありません。
たいていのスカラデータ型は対応する配列型を持つはずです(すなわち、要素がスカラ型の標準varlena配列型で、スカラ型のpg_type
エントリのtyparray
フィールドから参照されているもの)。
genbki.pl
はたいていの場合に配列型に対するpg_type
エントリも自動的に生成できます。
この機能を使うには、スカラ型のpg_type
エントリにarray_type_oid =>
というメタデータフィールドを記述して、配列型に使用するOIDを指定します。
自動的にそのOIDが書かれるため、このときnnnn
typarray
フィールドを省いてもよいです。
生成された配列型の名前は、スカラ型の名前の手前にアンダースコアを付けたものです。
配列エントリの他のフィールドは、pg_type.h
でBKI_ARRAY_DEFAULT(
注釈から充当され、もし無ければスカラ型からコピーされます。
(value
)typalign
に対する特別な場合もあります。)
さらに両エントリのtypelem
およびtyparray
フィールドは相互参照するように設定されます。
カタログデータファイルを更新する共通の作業を実施するためのもっとも簡単な方法の提案を示します。
カタログにデフォルト付きの新しいカラムを追加する.
BKI_DEFAULT(
注釈付きでヘッダーファイルにカラムを追加します。
非デフォルト値が必要な既存の行に対してのみフィールドを追加によるデータファイルの調整が必要です。
value
)
デフォルト値を持たない既存のカラムにデフォルト値を追加する.
BKI_DEFAULT
注釈をヘッダーファイルに追加し、冗長になったフィールドエントリを削除するためにmake reformat-dat-files
を実行します。
デフォルト値の有無にかかわらず、カラムを削除する.
ヘッダーからカラムを削除し、make reformat-dat-files
を実行して不要になったフィールドエントリを削除します。
既存のデフォルト値を変更もしくは削除する.
現在のデータが正しく解釈されなくなるため、単にヘッダーファイルを変更することはできません。
まずmake expand-dat-files
を実行し、すべてのデフォルト値が明示的に挿入されるようにデータファイルを書き換えます。
次にBKI_DEFAULT
注釈を変更もしくは削除し、make reformat-dat-files
を実行して余分のフィールドを再び削除します。
特定の目的のための大量の編集:
reformat_dat_file.pl
を使って色々な大量の変更を実施できます。
一度限りのコードを挿入できることを示すブロックコメントを見つけます。
次の例では、pg_proc
中の2つの論理値型フィールドを一つの文字フィールドに統合します。
デフォルトがある新しいカラムをpg_proc.h
に追加します。
+ /* see PROKIND_ categories below */ + char prokind BKI_DEFAULT(f);
臨機応変に適当な値を挿入するために、reformat_dat_file.pl
を元に新しいスクリプトを作ります。
- # At this point we have the full row in memory as a hash - # and can do any operations we want. As written, it only - # removes default values, but this script can be adapted to - # do one-off bulk-editing. + # One-off change to migrate to prokind + # Default has already been filled in by now, so change to other + # values as appropriate + if ($values{proisagg} eq 't') + { + $values{prokind} = 'a'; + } + elsif ($values{proiswindow} eq 't') + { + $values{prokind} = 'w'; + }
スクリプトを実行します。
$ cd src/include/catalog $ perl rewrite_dat_with_prokind.pl pg_proc.dat
この時点でpg_proc.dat
にはprokind
、proisagg
、proiswindow
のすべての3つのカラムがありますが、非デフォルト値を持つ行だけに表れます。
pg_proc.h
から古いカラムを削除します。
- /* is it an aggregate? */ - bool proisagg BKI_DEFAULT(f); - - /* is it a window function? */ - bool proiswindow BKI_DEFAULT(f);
最後に、make reformat-dat-files
を実行してpg_proc.dat
から不要になった古いエントリを削除します。
さらなる大量編集スクリプトの例については、https://www.postgresql.org/message-id/CAJVSVGVX8gXnPm+Xa=DxR7kFYprcQ1tNcCT5D0O3ShfnM6jehA@mail.gmail.comに付随するconvert_oid2name.pl
とremove_pg_type_oid_symbols.pl
を見てください。