この節では PostgreSQL のテーブルおよびインデックスで使われるページフォーマットの概略について説明します。 (インデックスアクセスメソッドはこのページフォーマットを使用する必要はありません。 現在では、すべてのインデックスメソッドがこの基本フォーマットを使用してはいます。しかし、インデックスメタページに保持されるデータは通常、アイテムレイアウトルールに正確には従っていません。) TOAST のテーブルとシーケンスは、通常のテーブルと同様にフォーマットされています。
以下の説明では 1 バイト は 8 ビットからなることを前堤としています。 更に、アイテムという単語は、ページに格納される個別のデータ値のことを指しています。 テーブル内では、アイテムはタプル(行)です。インデックス内では、アイテムはインデックスのエントリです。
Table 7-1 はページの基本レイアウトを示しています。 各ページには 5 つの部分があります。
Table 7-1. サンプルページレイアウト
アイテム | 説明 |
---|---|
PageHeaderData | 長さは 20 バイト。フリースペースポインタを含む、ページについての一般情報です。 |
ItemPointerData | 実際のアイテムを指す(オフセットと長さの)ペアの配列です。 |
フリースペース | 割り当てられていない空間です。 すべての新規タプルはここから割り当てられます(通常は最後から)。 |
アイテム | 実際のアイテムそのものです。 |
特別な空間 | インデックスアクセスメソッド特有のデータです。 異なるメソッドは異なるデータを格納します。 通常のテーブルでは空です。 |
それぞれのページの最初の 20 バイトはページヘッダ(PageHeaderData)から構成されています。 そのフォーマットを Table 7-2 に説明してあります。 最初の 2 つのフィールドは WAL に関連する項目を扱います。 その後に、2 バイトの整数フィールドが 3 つ続きます(pd_lower、pd_upper、pd_special)。 これらは、割り当てられていない空間の始まり、割り当てられていない空間の終わり、そして特別な空間の始まりのバイトオフセットを表しています。
Table 7-2. PageHeaderData のレイアウト
フィールド | 型 | 長さ | 説明 |
---|---|---|---|
pd_lsn | XLogRecPtr | 8 バイト | LSN: xlog の最後のバイトのつぎのバイト |
pd_sui | StartUpID | 4 バイト | 最終変更の SUI (現在ではヒープ AM でのみ使用) |
pd_lower | LocationIndex | 2 バイト | フリースペースの始まりに対するオフセット。 |
pd_upper | LocationIndex | 2 バイト | フリースペースの終わりに対するオフセット。 |
pd_special | LocationIndex | 2 バイト | 特別な空間の始まりに対するオフセット。 |
pd_pagesize_version | uint16 | 2 バイト | ページサイズおよびレイアウトのバージョン番号の情報。 |
詳細情報については src/include/storage/bufpage.h を参照してください。
特別な空間は、ページ初期化時にページの最後に割り当てられる区域で、アクセスメソッド特有の情報を持っています。 ページヘッダの最後の 2 バイトである pd_pagesize_version は、ページサイズとバージョンインジケータの両方を格納します。 PostgreSQL 7.3 以降のバージョン番号は 1 です。それより前のリリースのバージョン番号は 0 です。(基本的なページレイアウトやヘッダのフォーマットは変更されていませんが、ヒープタプルヘッダのレイアウトが変更されました。) ページサイズは基本的に、照合用としてのみ存在しています。同一インストールでの複数のページサイズはサポートされていません。
ページヘッダに続くのはアイテム識別子(ItemIdData)です。識別子ごとに 4 バイトを必要とします。アイテム識別子は、アイテムが開始されるバイトオフセット、バイトで表されたその長さ、そして表示に影響する属性ビットの集合を持っています。 新しいアイテム識別子は必要に応じて、割り当てられていない空間の最初から割り当てられます。 存在するアイテム識別子の数は、新しい識別子を割り当てるために増加される pd_lower を見ることで判断できます。 アイテム識別子は解放されるまで動かされることがないので、アイテム自体がフリースペースをコンパクトにするためにページ上で移動される場合でも、そのインデックスはアイテムを参照するために長期にわたって使うことができます。 実際、PostgreSQLが作る、アイテムへのポインタ(ItemPointer、CTIDともいいます)はページ番号とアイテム識別子のインデックスによって構成されています。
アイテムそれ自体は、割り当てられていない空間の最後から順番に割り当てられた空間に格納されます。 正確な構造は、テーブルに何を含めるかによって異なります。 テーブルとシーケンスの両方が、以下で説明する HeapTupleHeaderData という構造を使用します。
最後のセクションは、アクセスメソッドが格納しようとするものを何でも含めることのできる「特別なセクション」です。 通常のテーブルでは、これはまったく使用されません(ページサイズを同じにするために pd_special を設定することで示されます)。
テーブルのタプルはすべて、同じ方法で構成されています。 固定サイズのヘッダ(ほとんどのマシンで 23 バイトを占有します)があり、その後にオプションのヌルビットマップ、オプションのオブジェクト ID フィールド、および他のユーザデータが続きます。 ヘッダについては Table 7-3 で説明しています。 実際のユーザデータ(タプルのフィールド)は、常にプラットフォームの MAXALIGN 距離の倍数である t_hoff で示されるオフセットから始まります。 ヌルビットマップは HEAP_HASNULL ビットが t_infomask 内で設定されている場合にのみ存在します。 存在する場合は、固定ヘッダのすぐ後ろから始まり、データ列ごとに 1 ビットとするのに十分なバイト数を占有します(合計すると、t_natts のビット数となります)。 このビットのリスト内では、1 ビットはヌルでないことを示します。0 ビットはヌルです。 このビットマップが存在しない場合、すべての列がヌルでないとみなされます。 オブジェクト ID は HEAP_HASOID ビットが t_infomask 内で設定されている場合にのみ存在します。 存在する場合、これは t_hoff 境界の直前に現れます。 t_hoff を MAXALIGN の倍数とするために必要なパッドはすべて、ヌルビットマップとオブジェクト ID の間に現れます。(このことにより、オブジェクト ID のアライメントが確実に適切になります。)
Table 7-3. HeapTupleHeaderData のレイアウト
フィールド | 型 | 長さ | 説明 |
---|---|---|---|
t_xmin | TransactionId | 4 バイト | XID スタンプを挿入 |
t_cmin | CommandId | 4 バイト | CID スタンプを挿入(t_xmax とオーバーレイ) |
t_xmax | TransactionId | 4 バイト | XID スタンプを削除 |
t_cmax | CommandId | 4 バイト | CID スタンプを削除(t_xvac とオーバーレイ) |
t_xvac | TransactionId | 4 バイト | VACUUM 操作移動タプルの XID |
t_ctid | ItemPointerData | 6 バイト | このタプルまたは最新タプルの現在の TID |
t_natts | int16 | 2 バイト | 属性の数 |
t_infomask | uint16 | 2 バイト | さまざまなフラグ |
t_hoff | uint8 | 1 バイト | ユーザデータに対するオフセット |
詳細情報については src/include/access/htup.h を参照してください。
実際のデータの解釈は、他のテーブル(ほとんどの場合、pg_attribute)から取得された情報でのみ行うことができます。 特定のフィールドは、attlen および attalign です。 フィールドの幅が固定されていて NULL が存在しない場合を除き、特定の属性を直接取得する方法はありません。 この仕組みはすべて、heap_getattr、fastgetattr および heap_getsysattr の関数にラップされています。
データを読むためには、それぞれの属性を順番に検査する必要があります。 まず、ヌルビットマップに従ってフィールドが NULL かどうかをチェックします。 もし NULL であれば、次に進みます。 次に、アライメントが正しいことを確認してください。 フィールドの幅が固定されていれば、すべてのバイトが単純に配置されます。 可変長のフィールド(attlen == -1)の場合はもう少し複雑で、varattrib 可変長構造が使用されます。 フラグによって、データはインライン、圧縮済み、または別のテーブル(TOAST)のいずれかとなります。