他のバージョンの文書 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

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

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

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

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

void
ambuildempty (Relation indexRelation);

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

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

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

indexUnchanged真偽値はインデックス付されるタプルの性質に関するヒントを与えます。 真なら、そのタプルは既存のインデックス中のタプルと重複しています。 新しいタプルは論理的に変わっていない後継であるMVCCタプルバージョンです。 これはUPDATEの実行によりインデックス対象のどの列も変更されず、それにもかかわらずインデックスにおいて新しいバージョンを要求する場合に起こります。 インデックスAMは、同じ論理的な行の多くのバージョンが蓄積されるときに、インデックスのある部分にボトムアップインデックス削除を適用するかどうかを決定するためにこのヒントを使うことができます。 非キー列を更新してもindexUnchangedの値には影響がないことに留意してください。

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

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

SQL文の中で、インデックスAMがインデックスへの連続的な挿入をまたがってデータをキャッシュすることが望ましい場合は、indexInfo->ii_Contextにメモリを確保し、そのデータへのポインタをindexInfo->ii_AmCache(初期値は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です。) これにより、アクセスメソッドは操作全体に跨った統計情報を計算することができます。 典型的に、渡されたstatsがNULLでない場合、ambulkdeleteは同じ構造体を変更し、返します。

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

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

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

bool
amcanreturn (Relation indexRelation, int attno);

列のインデックスされた元の値を返すことにより、そのインデックスが指定された列でインデックスオンリースキャンをサポート可能かどうかを判断します。 属性番号は1始まり、すなわち最初の列のattnoは1です。 インデックスオンリースキャンがサポートされている場合は真が返され、サポートされていない場合は偽が返ります。 取得できない列がinclude列である意味はないので、この関数は(サポートされていれば)include列に対しては常に真を返すでしょう。 アクセスメソッドがインデックスオンリースキャンをサポートしていない場合、IndexAmRoutine構造体のamcanreturnフィールドをNULLにセットすることができます。

void
amcostestimate (PlannerInfo *root,
                IndexPath *path,
                double loop_count,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation,
                double *indexPages);

インデックススキャンのコストを推定します。 この関数については後述の62.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を返しても問題ありません。

bool
amproperty (Oid index_oid, int attno,
            IndexAMProperty prop, const char *propname,
            bool *res, bool *isnull);

ampropertyメソッドにより、インデックスメソッドはpg_index_column_has_propertyおよび関連する関数のデフォルトの動作を上書きすることができます。 インデックスアクセスメソッドがインデックスの属性の問い合わせについて特別な動作をしないのなら、IndexAmRoutine構造体のampropertyフィールドはNULLにすることができます。 そうでなければ、ampropertypg_indexam_has_propertyの呼び出しに対し、index_oidattnoをいずれもゼロにして、pg_index_has_propertyの呼び出しに対してindex_oidが有効、attnoがゼロで、あるいはpg_index_column_has_propertyの呼び出しに対してindex_oidが有効、attnoが1以上で呼び出されます。 propは検査対象の属性を指定する列挙型の値、propnameは元の属性の名称の文字列です。 コアのコードが属性名を認識しない場合、propAMPROP_UNKNOWNになります。 アクセスメソッドはカスタム属性名を定義して、マッチするものをpropnameで確認する(コアコードとの一貫性のため、pg_strcasecmpを使ってください)ことができます。 コアコードに既知の名前については、propを検査する方が良いです。 ampropertyメソッドがtrueを返すなら、それは属性検査の結果が決定したということで、*resを返すべき論理値にセットするか、NULLを返すために*isnulltrueにセットするかしなければなりません。 (どちらの参照変数も、呼び出しの前にfalseに初期化されます。) ampropertyメソッドがfalseを返すなら、コアコードは属性検査の結果を決定するために、通常の手続きを進めます。

順序付け演算子をサポートするアクセスメソッドは、AMPROP_DISTANCE_ORDERABLEの属性検査を実装する必要があります。 なぜなら、コアコードはそれをどうすれば良いか知らないため、NULLを返すからです。 コアコードのデフォルトの動作であるインデックスのオープンとamcanreturnの呼び出しよりも安価にできるのであれば、AMPROP_RETURNABLEの検査を実装するのは利点となります。 その他のすべての標準属性に対しては、デフォルトの動作が満足できるもののはずです。

char *
ambuildphasename (int64 phasenum);

指定されたビルドフェーズ番号のテキスト名を返します。 フェーズ番号は、pgstat_progress_update_paramインタフェースを介してインデックス構築中に報告されたものです。 それから、フェーズ名はpg_stat_progress_create_indexビューで公開されます。

bool
amvalidate (Oid opclassoid);

指定の演算子クラスについて、アクセスメソッドが合理的に可能な範囲でカタログエントリを検証します。 例えば、これには必要なすべてのサポート関数が提供されていることのテストが含まれるかもしれません。 amvalidate関数は演算子クラスが無効なときは偽を返さなければなりません。 問題があれば典型的にはINFOレベルでereportメッセージにより報告されます。

void
amadjustmembers (Oid opfamilyoid,
                 Oid opclassoid,
                 List *operators,
                 List *functions);

アクセスメソッドが合理的に可能な範囲で提案された新しい演算子族の演算子と関数メンバーを検証し、デフォルトが不十分なら依存型を設定します。 これはCREATE OPERATOR CLASSALTER OPERATOR FAMILY ADDの実行中に呼び出されます。 後者の場合、opclassoidInvalidOidです。 List引数はamapi.hで定義されているOpFamilyMember構造体のリストです。 この関数で実施されるテストは典型的にはamvalidateが行うテストのサブセットです。 なぜなら、amadjustmembersはメンバーの集合のすべてを観察しているとは仮定することができないからです。 たとえば、サポート関数の呼び出し形式を検証することは妥当ですが、必要なすべてのサポート関数が提供されていることを検証するのは妥当ではないからです。 問題が発生すればどの場合でもエラーが生じます。 OpFamilyMember構造体の依存性に関するフィールドに、CREATE OPERATOR CLASSの場合にはopclassにコアコードがハード依存性で初期化します。ALTER OPERATOR FAMILY ADDならばopfamilyをソフト依存性で初期化します。 それ以外の振る舞いがより適正ならば、amadjustmembersでこれらのフィールドを調整することができます。 たとえば、GIN、GiST、SP-GiSTのようなインデックス形式では演算子とopclassの関連性が相対的低く、演算子メンバーの追加削除を自由に行うことが合理的であるため、これらの演算子メンバーにおいてはopfamilyに常にソフト依存性が設定されます。 また、必要ならば削除可能にするために、追加のサポート関数にソフト依存性を設定するのが一般的です。

当然ながらインデックスの目的は、よく修飾子スキャンキーと呼ばれる、インデックス可能なWHERE条件を満たすタプルのスキャンをサポートすることです。 インデックススキャンのセマンティクスは後の62.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に渡したキー演算子、順序付け演算子の個数より多くを使用することはできないことに注意してください。 実際には、ネステッドループ結合によって新しい外部タプルが選択され、同じスキャンキー構造体で新しいキー比較値が必要とされた場合に、この再起動機能は使用されます。

bool
amgettuple (IndexScanDesc scan,
            ScanDirection direction);

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

そのインデックスがインデックスオンリースキャンをサポートしている場合(つまりamcanreturnがすべての列に対して真を返す場合)、そのアクセスメソッドはスキャンが成功したならばscan->xs_want_itupも確認し、それが真の場合、そのインデックスエントリに対応する元のインデックスされたデータを返さなければなりません。 amcanreturnが列に対して偽を返す場合、その列はNULLとして返されます。 返却されるデータは、scan->xs_itupdescタプルディスクリプタとともにscan->xs_itupに格納されたIndexTupleポインタの形式か、あるいは、scan->xs_hitupdescタプルディスクリプタとともにscan->xs_hitupに格納されたHeapTupleポインタの形式です。 (後者の形式は、再構成されたデータがIndexTupleに収まらない場合に使用するべきです。) どちらの場合でも、そのポインタが参照するデータの管理はアクセスメソッドの責任です。 データは少なくともamgettupleamrescanまたはamendscanによってスキャンされるまでよい状態を保たなくてはなりません。

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

int64
amgetbitmap (IndexScanDesc scan,
             TIDBitmap *tbm);

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

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

void
amendscan (IndexScanDesc scan);

スキャンを停止し、リソースを解放します。 scan構造体自体は解放すべきではありません。 アクセスメソッドで内部的に取られたロックやピンは、ambeginscanや他のスキャン関連の関数により確保されたメモリと同様に解放しなければなりません。

void
ammarkpos (IndexScanDesc scan);

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

ammarkpos関数はアクセスメソッドが順序付けされたスキャンをサポートする場合にのみ提供する必要があります。 そうでなければ、そのIndexAmRoutine構造体のammarkposフィールドはNULLに設定しても構いません。

void
amrestrpos (IndexScanDesc scan);

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

amrestrpos関数はアクセスメソッドが順序付けされたスキャンをサポートする場合にのみ提供する必要があります。 そうでなければ、そのIndexAmRoutine構造体のamrestrposフィールドはNULLに設定しても構いません。

通常のインデックススキャンのサポートに加え、ある種のインデックスは、複数のバックエンドが協調してインデックススキャンを実行するパラレルインデックススキャンをサポートすることができます。 インデックスアクセスメソッドは、協調するプロセスが、通常の非パラレルインデックススキャンが実行対象とする行のサブセットを返しつつ、しかもそれらのサブセットの合計が、通常の非パラレルインデックススキャンが返すタプルの集合と同じになるように調整しなければなりません。 それだけでなく、パラレルスキャンが返すタプル全体の順序付けが想定されていない場合でも、協調するバックエンドが返すサブセットのタプルの順序付けは、要求された順序付けと一致しなければなりません。 パラレルインデックススキャンをサポートするために、以下の関数を実装することができます。

Size
amestimateparallelscan (void);

パラレルスキャンを実行するために、アクセスメソッドによって必要とされる動的共有メモリのバイト数を推測し、返します。 (この数値は、ParallelIndexScanDescDataのAM独立データに必要となる量に追加するための値であり、それを置き換えるものではありません。)

パラレルスキャンをサポートしない、あるいはメモリ領域への追加のバイト数が0のアクセスメソッドでは、この関数を実装する必要はありません。

void
aminitparallelscan (void *target);

この関数は、パラレルスキャンの最初に動的共有メモリを初期化するために呼ばれます。 targetは、前もってamestimateparallelscanが返したバイト数を少なくとも持つ領域を指し、この関数はその分だけのスペースを使って必要なデータを保管することができます。

パラレルスキャンをサポートしない、あるいは共有メモリスペースの初期化が必要ないアクセスメソッドでは、この関数を実装する必要はありません。

void
amparallelrescan (IndexScanDesc scan);

実装された場合、この関数はパラレルインデックススキャンを再起動しなければならない時に呼ばれます。 この関数は、aminitparallelscanが設定した共有状態を初期化し、スキャンが最初から再開できるようにします。