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

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

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

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

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を必要とする唯一の状況です。 詳細は項49.5を参照してください。 インデックスが挿入された場合、TRUEが返されます。 挿入されなかった場合、FALSEが返されます。 (FALSEという結果はエラー条件を表すものではありませんが、インデックスアクセスメソッドがNULLに対するインデックスを拒絶する場合にも使用されます。)

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です。) これにより、AMは操作全体に跨った統計情報を計算することができます。 典型的に、渡されたstatsがNULLでない場合、ambulkdeleteは同じ構造体を変更し、返します。

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

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

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

インデックススキャンのコストを推定します。 この関数については後述の項49.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条件を満たすタプルのスキャンをサポートすることです。 インデックススキャンのセマンティックスは、後で項49.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を同じインデックススキャン内で使用することはできません。 項49.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);

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

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