インデックスアクセスメソッドが提供しなければならない、インデックス構築および保守関数を以下に示します。
void ambuild (Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo);
新しいインデックスを構築します。
空のインデックスリレーションが物理的に作成されます。
これは、アクセスメソッドが必要とする何らかの固定データと、テーブル内に既存のすべてのタプルに対応する項目が書き込まれなければなりません。
通常、ambuild
関数はIndexBuildHeapScan()
を呼び出し、既存のタプルをテーブルからスキャンし、インデックスに挿入しなければならないキーを計算します。
bool aminsert (Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRelation, bool check_uniqueness);
既存のインデックスに新しいタプルを挿入します。 values配列と isnull配列がインデックスされるキー値を提供するもので、heap_tidがインデックスされるTIDです。 アクセスメソッドが一意なインデックスをサポートする場合(そのpg_am.amcanuniqueが真の場合)、check_uniquenessは真を取ることができます。 この場合、アクセスメソッドでは、競合する行が存在しないことを検証しなければなりません。 通常これは、アクセスメソッドがheapRelationを必要とする唯一の状況です。 詳細は項48.5を参照してください。 インデックスが挿入された場合、TRUEが返されます。 挿入されなかった場合、FALSEが返されます。 (FALSEという結果はエラー条件を表すものではありませんが、インデックスアクセスメソッドがNULLに対するインデックスを拒絶する場合にも使用されます。)
IndexBulkDeleteResult * ambulkdelete (Relation indexRelation, IndexBulkDeleteCallback callback, void *callback_state);
インデックスからタプル(複数可)を削除します。 これは"一括削除"操作を行いますが、インデックス全体をスキャンし、各項目に対して削除すべきかどうか検査を行うように実装されることが想定されています。 渡されるcallback関数は、callback(TID, callback_state) returns boolという形で、参照用TIDで識別されるインデックス項目を削除すべきかどうか決定するために呼び出される可能性があります。 NULLまたはpallocした削除操作の影響に関する統計情報を含む構造体を返さなければなりません。
IndexBulkDeleteResult * amvacuumcleanup (Relation indexRelation, IndexVacuumCleanupInfo *info, IndexBulkDeleteResult *stats);
VACUUM操作(一回以上のambulkdelete
呼び出し)後の整理を行います。
インデックスアクセスメソッドはこの関数を提供する必要はありません(この場合、pg_am内の項目はゼロでなければなりません)。
提供された場合、通常これは空のインデックスページの回収など一括整理のために使用されます。
infoは、統計情報報告用のメッセージレベルなど、追加的な引数を提供します。
また、statsは、直前のambulkdelete
呼び出しが返したものです。
amvacuumcleanup
は、終了する前に、この構造体を置き換えたり変更したりする可能性があります。
含まれる統計情報が、VERBOSE付きのVACUUMで報告されます。
当然ながらインデックスの目的は、よく修飾子やスキャンキーと呼ばれる、インデックス可能なWHERE条件を満たすタプルのスキャンをサポートすることです。 インデックススキャンのセマンティックスは、後で項48.3でより詳しく説明します。 インデックスアクセスメソッドが提供しなければならないスキャン関連の関数を以下に示します。
IndexScanDesc ambeginscan (Relation indexRelation, int nkeys, ScanKey key);
新しいスキャンを開始します。
(nkeys長の)key配列は、インデックススキャン用のスキャンキー(複数可)を記述します。
結果は、pallocした構造体でなければなりません。
実装上の理由により、インデックスアクセスメソッドはRelationGetIndexScan()
呼び出しによってこの構造体を作成しなければなりません。
ほとんどの場合、ambeginscan
自体はこの呼び出しの他にはほとんど何も行いません。
インデックス起動の興味深い部分は、amrescan
にあります。
boolean amgettuple (IndexScanDesc scan, ScanDirection direction);
指定されたスキャン内から指定された方向(インデックス内の前方または後方)で次のタプルを取り出します。 タプルを取り出した場合はTRUEを返します。 一致するタプルが残っていない場合はFALSEを返します。 TRUEの場合、そのタプルのTIDがscanに格納されます。 "成功"とは、単にインデックスにスキャンキーに一致する項目があったことを意味しているだけです。 タプルが必ずヒープ内に存在することや、呼び出し元のスナップショットの試験を通過したことを意味してはいません。
boolean amgetmulti (IndexScanDesc scan, ItemPointer tids, int32 max_tids, int32 *returned_tids);
指定されたスキャンから複数のタプルを取り出します。
スキャンを継続すべき場合にはTRUEを、一致するタプルが残っていない場合にはFALSEを返します。
tidsは、呼び出し元が提供するmax_tids個のItemPointerDataレコードの配列を指し示します。
この呼び出しは一致したタプルのTIDをここに格納します。
*returned_tidsは実際に返されるTIDの数に設定されます。
これはmax_tidsより少ないかもしれません。また、戻り値がTRUEであっても0となるかもしれません。
(この規定により、アクセスメソッドは、例えばページ境界などでスキャンを効率的に停止することができます。)
amgetmulti
およびamgettuple
を同じインデックススキャン内で使用することはできません。
項48.3で説明した通り、amgetmulti
を使用する場合には他にも制限があります。
void amrescan (IndexScanDesc scan, ScanKey key);
指定されたスキャンを再起動します。
スキャンキーを新しくすることもできます。
(古いキーのまま継続するには、keyにNULLを渡します。)
キーの数を変更することはできないことに注意してください。
実際には、入れ子状ループ結合によって新しい外部タプルが選択され、同じスキャンキー構造体で新しいキー比較値が必要とされた場合に、この再起動機能は使用されます。
再スキャンだけではなくインデックススキャンの初期設定にも使用されますので、この関数はまた、RelationGetIndexScan()
からも呼び出されます。
void amendscan (IndexScanDesc scan);
スキャンを停止し、リソースを解放します。 scan構造体自体は解放すべきではありません。 アクセスメソッドで内部的に取られたロックやピンは解放しなければなりません。
void ammarkpos (IndexScanDesc scan);
現在のスキャン位置を記録します。 アクセスメソッドは1スキャン当たり1つの記録済みスキャンのみをサポートしなければなりません。
void amrestrpos (IndexScanDesc scan);
もっとも最近に記録された位置にスキャンを戻します。
void amcostestimate (PlannerInfo *root, IndexOptInfo *index, List *indexQuals, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation);
インデックススキャンのコストを推定します。 この関数は後述の項48.6で詳しく説明されています。
簡便性のために、 任意のインデックスアクセスメソッド関数のpg_proc項目は、正確な引数の数を示さなければなりません。 しかし、それらはすべてinternal型として宣言します。 (引数のほとんどがSQLでは未知の型を持つため、ユーザがこうした関数を直接呼び出すことを防ぐことがこの理由です。) 戻り値の型は、void、internal、booleanのいずれかで適切に宣言されます。