52.2. インデックスアクセスメソッド関数

インデックスアクセスメソッドが提供しなければならない、インデックス構築および保守関数を以下に示します。

IndexBuildResult *
ambuild (Relation heapRelation,
         Relation indexRelation,
         IndexInfo *indexInfo);

新しいインデックスを構築します。 空のインデックスリレーションが物理的に作成されます。 これは、アクセスメソッドが必要とする何らかの固定データと、テーブル内に既存のすべてのタプルに対応する項目が書き込まれなければなりません。 通常、ambuild関数はIndexBuildHeapScan()を呼び出し、既存のタプルをテーブルからスキャンし、インデックスに挿入しなければならないキーを計算します。 この関数は、新しいインデックスに関する統計情報を含むpallocされた構造体を返さなければなりません。

void
ambuildempty (Relation indexRelation);

空のインデックスを構築し、それを指定されたリレーションの初期フォーク(INIT_FORKNUM)に書き出します。 このメソッドはログを取らないテーブルに対してのみ呼び出されます。 初期フォークに書き出された空のインデックスは、サーバの再起動の度に主リレーションフォークにコピーされます。

bool
aminsert (Relation indexRelation,
          Datum *values,
          bool *isnull,
          ItemPointer heap_tid,
          Relation heapRelation,
          IndexUniqueCheck checkUnique);

既存のインデックスに新しいタプルを挿入します。 values配列とisnull配列がインデックスされるキー値を提供するもので、heap_tidがインデックスされるTIDです。 アクセスメソッドが一意なインデックスをサポートする場合(そのpg_am.amcanuniqueが真の場合)、checkUniqueは実行する一意性検査の種類を示します。 これは一意性制約が遅延可能か否かによって変わります。 項52.5を参照してください。 通常アクセスメソッドは、一意性検査を行う時にheapRelationパラメータのみを必要とします (タプルの有効性を検証するためにヒープ内を検索しなければなりません)。

checkUniqueUNIQUE_CHECK_PARTIALの場合、関数の論理型の結果値で十分です。 この場合、真の結果は新しい項目は一意であることが確認されたことを、一方偽の結果は一意でない可能性があること(遅延一意性検査を予定しなければならないこと)を意味します。 他の場合では、一定の偽という結果が推奨されます。

一部のインデックスではすべてのタプルをインデックス付けしない可能性があります。 タプルがインデックス付けされない場合、aminsertは何も行わずに戻らなければなりません。

IndexBulkDeleteResult *
ambulkdelete (IndexVacuumInfo *info,
              IndexBulkDeleteResult *stats,
              IndexBulkDeleteCallback callback,
              void *callback_state);

インデックスからタプル(複数可)を削除します。 これは"一括削除"操作を行いますが、インデックス全体をスキャンし、各項目に対して削除すべきかどうか検査を行うように実装されることが想定されています。 渡されるcallback関数は、callback(TID, callback_state) returns boolという形で、参照用TIDで識別されるインデックス項目を削除すべきかどうか決定するために呼び出さなければなりません。 NULLまたはpallocした削除操作の影響に関する統計情報を含む構造体を返さなければなりません。 amvacuumcleanupに渡さなければならない情報がなければ、NULLを返しても問題ありません。

maintenance_work_memの制限により、多くのタプルが削除される時、ambulkdeleteを複数回呼び出す必要があるかもしれません。 stats引数は、このインデックスに対する前回の呼び出し結果です。 (VACUUM操作における最初の呼び出しではこれはNULLです。) これにより、アクセスメソッドは操作全体に跨った統計情報を計算することができます。 典型的に、渡されたstatsがNULLでない場合、ambulkdeleteは同じ構造体を変更し、返します。

IndexBulkDeleteResult *
amvacuumcleanup (IndexVacuumInfo *info,
                 IndexBulkDeleteResult *stats);

VACUUM操作(0回以上のambulkdelete呼び出し)後の整理を行います。 これは、インデックス統計情報を返す以上の処理を行う必要はありません。 しかし、空のインデックスページの回収などの一括整理を行う可能性があります。 statsは最後のambulkdelete呼び出しが返したものです。 削除する必要があるタプルが存在しなかったためにambulkdeleteが呼び出されなかった場合はNULLとなります。 結果はNULLでなければ、pallocされた構造体でなければなりません。 含まれる統計情報はpg_classを更新するために使用され、また、VERBOSEが指定されたVACUUMによって報告されます。 VACUUM操作の間にインデックスがまったく変わらなかった場合はNULLを返しても問題ありません。 しかし、そうでなければ正しい統計情報を返さなければなりません。

PostgreSQL 8.4の時点で、amvacuumcleanupANALYZE操作の完了時点にも呼び出されます。 この場合、statsは常にNULLで、戻り値はまったく無視されます。 この事象はinfo->analyze_onlyを検査することで識別されます。 アクセスメソッドがそのような呼び出しで挿入後の整理以外何もしないように、そしてそれは自動バキュームワーカプロセスのみであるようにすることを推奨します。

void
amcostestimate (PlannerInfo *root,
                IndexOptInfo *index,
                List *indexQuals,
                List *indexOrderBys,
                RelOptInfo *outer_rel,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation);

インデックススキャンのコストを推定します。 この関数については後述の項52.6で説明します。

bytea *
amoptions (ArrayType *reloptions,
           bool validate);

インデックス用のreloptionsの解析と検証を行います。 インデックスに非NULLのreloptions配列が存在する場合にのみ呼び出されます。 reloptionsは、name=value形式の項目からなる、text型の配列です。 この関数はbytea型の値を生成しなければならず、この値はインデックスのrelcache項目のrd_optionsフィールドにコピーされます。 bytea型の値の内容はアクセスメソッドが独自に定義できるように開放されています。 標準のアクセスメソッドのほとんどはすべてStdRdOptions構造体を使用します。 validateが真の場合、何らかのオプションが認識できなかった場合や無効な値が存在した場合、この関数は適切なエラーメッセージを報告しなければなりません。 validateが偽の場合、無効な項目は単に無視されます。 (読み込みオプションが既にpg_catalogに格納されている場合validateは偽です。 アクセスメソッドがそのオプション用の規則を変更した場合にのみ、無効な項目が検出されます。 そして、その場合、古い項目を無視することが適切です。) デフォルトの動作を行わせたい場合はNULLを返しても問題ありません。

当然ながらインデックスの目的は、よく修飾子スキャンキーと呼ばれる、インデックス可能なWHERE条件を満たすタプルのスキャンをサポートすることです。 インデックススキャンのセマンティックスは後の項52.3でより詳しく説明します。 インデックスアクセスメソッドは"単純"インデックススキャン、"ビットマップ"インデックススキャン、またはこれら双方を提供します。 インデックスアクセスメソッドが提供しなければならない、もしくは提供する可能性のあるスキャン関連の関数を以下に示します。

IndexScanDesc
ambeginscan (Relation indexRelation,
             int nkeys,
             int norderbys);

インデックススキャンを準備します。 nkeysおよびnorderbysパラメータは、スキャンで使用される等価性演算子と順序付け演算子の個数を表します。 これらは領域を割り当てる目的で便利かもしれません。 スキャンキーの実値がまだ提供されていないことに注意してください。 結果はpallocした構造体でなければなりません。 実装上の理由により、インデックスアクセスメソッドはRelationGetIndexScan()呼び出しによってこの構造体を作成しなければなりません。 ほとんどの場合、ambeginscanはこの呼び出しとおそらくロックの獲得の他にはほとんど何も行いません。 インデックススキャンを始める際の興味深い部分は、amrescanにあります。

void
amrescan (IndexScanDesc scan,
          ScanKey keys,
          int nkeys,
          ScanKey orderbys,
          int norderbys);

インデックススキャンを起動または再起動します。 スキャンキーを新しくすることもできます。 (過去に渡されたキーを使用して再起動するには、keyorderbys、またはその両方にNULLを渡します。) ambeginscanに渡したキー演算子、順序付け演算子の個数より多くを使用することはできないことに注意してください。 実際には、入れ子状ループ結合によって新しい外部タプルが選択され、同じスキャンキー構造体で新しいキー比較値が必要とされた場合に、この再起動機能は使用されます。

boolean
amgettuple (IndexScanDesc scan,
            ScanDirection direction);

指定されたスキャン内から指定された方向(インデックス内の前方または後方)で次のタプルを取り出します。 タプルを取り出した場合は真を返します。 一致するタプルが残っていない場合は偽を返します。 真の場合、そのタプルのTIDがscanに格納されます。 "成功"とは、単にインデックスにスキャンキーに一致する項目があったことを意味しているだけです。 タプルが必ずヒープ内に存在することや、呼び出し元のスナップショットの試験を通過したことを意味してはいません。 成功の暁には、amgettuplescan->xs_recheckを真か偽かに設定しなければなりません。 偽の意味は、インデックス項目が確実にスキャンキーに一致することです。 真の意味は、これが確かなことではなく、スキャンキーで表示された条件がヒープタプルを取り出された後で再検査されなければならないことです。 この対策は"非可逆"インデックス演算子をサポートします。 再検査はスキャン条件のみに拡大適用されることに注意してください。 部分インデックス述語(もしあれば)はamgettuple呼び出し元で決して再検査されません。

amgettuple関数は、アクセスメソッドが"単純"インデックススキャンをサポートするときのみ提供される必要があります。 そうでなければ、pg_am行のamgettupleフィールドはゼロに設定されなければなりません。

int64
amgetbitmap (IndexScanDesc scan,
             TIDBitmap *tbm);

指定されたスキャンから全てのタプルを取り出し、呼び出し側が提供するTIDBitmapにそれらを付加します (つまり、既にビットマップ内にある集合とタプルIDの集合とのORを取ります)。 取り出されたタプル数が返されます(例えばいくつかのAMは重複を検出しませんので、これは単なる概算です)。 タプルIDをビットマップに挿入する間、amgetbitmapは特定のタプルIDに必要なスキャン条件の再検査を示すことが可能です。 これはamgettuplexs_recheck出力パラメータに類似しています。 注意:現在の実装においてこの機能の提供はビットマップそのものの非可逆格納を提供するのに結びついていて、したがって呼び出し側はスキャン条件と部分インデックスの述部(存在すれば)を再検査可能なタプルに対して再検査します。 とは言っても常に正しいとは限りません。 amgetmultiおよびamgettupleを同じインデックススキャン内で使用することはできません。 項52.3で説明した通り、amgetbitmapを使用する場合には他にも制限があります。

amgetbitmap関数はアクセスメソッドが"ビットマップ"インデックススキャンをサポートしている場合のみ必要です。 そうでなければ、pg_am行の中のamgetbitmapフィールドはゼロに設定されなければなりません。

void
amendscan (IndexScanDesc scan);

スキャンを停止し、リソースを解放します。 scan構造体自体は解放すべきではありません。 アクセスメソッドで内部的に取られたロックやピンは解放しなければなりません。

void
ammarkpos (IndexScanDesc scan);

現在のスキャン位置を記録します。 アクセスメソッドは1スキャン当たり1つの記録済みスキャンのみをサポートしなければなりません。

void
amrestrpos (IndexScanDesc scan);

もっとも最近に記録された位置にスキャンを戻します。

簡便性のために、インデックスアクセスメソッド関数のpg_proc項目は、正確な引数の数を示さなければなりません。 しかし、それらはすべてinternal型として宣言します。 (引数のほとんどがSQLでは未知の型を持つため、ユーザがこうした関数を直接呼び出すことを防ぐことがこの理由です。) 戻り値の型は、voidinternalbooleanのいずれかで適切に宣言されます。 唯一の例外はamoptionsです。 これは、text[]およびboolを取りbyteaを返すように正しく宣言しなければなりません。 この規定により、クライアントコードはamoptionsを実行してオプションの設定の有効性を検査することができます。