各インデックスメソッドはpg_amシステムカタログの行で説明されます。
pg_amエントリはアクセスメソッドの名前とハンドラ関数を指定します。
これらのエントリはSQLコマンドCREATE ACCESS METHODとDROP ACCESS METHODを使って、作成および削除することができます。
インデックスメソッドのハンドラ関数は、internal型の引数を1つ取り、疑似型index_am_handlerを返すものとして宣言しなければなりません。
引数は単にハンドラ関数がSQLコマンドから直接呼び出されるのを防ぐためのダミーの値です。
関数の結果は型IndexAmRoutineのpallocされた構造体でなければならず、そこにはインデックスアクセスメソッドを使用するためにコアコードが知っている必要のあるすべてのことが含まれています。
IndexAmRoutine構造体は、アクセスメソッドのAPI構造体とも呼ばれ、複数列のインデックスをサポートするかどうかなどといった、アクセスメソッドに関する様々な既定の属性を指定するフィールドが含まれます。
さらに重要なことに、この構造体にはアクセスメソッドのサポート関数へのポインタが含まれ、これによってインデックスにアクセスするためのすべての実際の処理が行われます。
これらのサポート関数は単なるCの関数で、SQLレベルでは見ることも呼び出すこともできません。
サポート関数は61.2で説明されています。
構造体IndexAmRoutineは以下のように定義されています。
typedef struct IndexAmRoutine
{
NodeTag type;
/*
* Total number of strategies (operators) by which we can traverse/search
* this AM. Zero if AM does not have a fixed set of strategy assignments.
*/
uint16 amstrategies;
/* total number of support functions that this AM uses */
uint16 amsupport;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
/* does AM support ORDER BY result of an operator on indexed column? */
bool amcanorderbyop;
/* does AM support backward scanning? */
bool amcanbackward;
/* does AM support UNIQUE indexes? */
bool amcanunique;
/* does AM support multi-column indexes? */
bool amcanmulticol;
/* does AM require scans to have a constraint on the first index column? */
bool amoptionalkey;
/* does AM handle ScalarArrayOpExpr quals? */
bool amsearcharray;
/* does AM handle IS NULL/IS NOT NULL quals? */
bool amsearchnulls;
/* can index storage data type differ from column data type? */
bool amstorage;
/* can an index of this type be clustered on? */
bool amclusterable;
/* does AM handle predicate locks? */
bool ampredlocks;
/* does AM support parallel scan? */
bool amcanparallel;
/* does AM support columns included with clause INCLUDE? */
bool amcaninclude;
/* type of data stored in index, or InvalidOid if variable */
Oid amkeytype;
/* interface functions */
ambuild_function ambuild;
ambuildempty_function ambuildempty;
aminsert_function aminsert;
ambulkdelete_function ambulkdelete;
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
amrescan_function amrescan;
amgettuple_function amgettuple; /* can be NULL */
amgetbitmap_function amgetbitmap; /* can be NULL */
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
aminitparallelscan_function aminitparallelscan; /* can be NULL */
amparallelrescan_function amparallelrescan; /* can be NULL */
} IndexAmRoutine;
使い易くするために、インデックスアクセスメソッドはまた、pg_opfamily、pg_opclass、pg_amopおよびpg_amproc内で定義される、複数の演算子族と演算子クラスを持ちます。
これらの項目により、プランナは、このアクセスメソッドのインデックスがどのような問い合わせ条件に対して使用できるかを決定することができます。
演算子族と演算子クラスについては、38.15で説明します。
これは本章を読む上で必要となる資料です。
個々のインデックスは、インデックスを物理的なリレーションとして記述するpg_class項目と、インデックスの論理的な内容、つまり、インデックスが持つインデックス列の集合とその列の意味を、関連する演算子クラスで再現されたものとして表すpg_index項目とで定義されます。
インデックス列(キー値)は、背後のテーブルの単純な列、あるいは、テーブル行に対する式とすることができます。
通常、インデックスアクセスメソッドはインデックスキー値が何を表すかについて考慮しません。
(常に計算済みのキー値として扱われます。)
しかし、pg_index内の演算子クラスの情報を深く考慮します。
この両方のカタログ項目は、インデックスに対するすべての操作に渡されるRelationデータ構造の一部としてアクセスすることができます。
IndexAmRoutineのフラグフィールドの中には、意味がわかりにくいものがあります。
amcanuniqueの必要条件は61.5で説明されています。
amcanmulticolフラグはアクセスメソッドが複数列に対するインデックスをサポートすることを表し、amoptionalkeyは、インデックス可能な制限句が最初のインデックス列に指定されていないスキャンを許可することを表します。
amcanmulticolが偽の場合、amoptionalkeyは基本的に、アクセスメソッドが制限句なしで完全なインデックススキャンをサポートするかどうかを表します。
複数列に対するインデックスをサポートするアクセスメソッドは、最初の列以降のすべてまたは一部の列に関する制限がなくてもスキャンをサポートしなければなりません。
しかし、最初のインデックス列にいくつかの制限を要求することは認められています。
これは、amoptionalkeyを偽に設定することで通知されます。
インデックスアクセスメソッドがamoptionalkeyを偽にする1つの理由は、NULLをインデックス付けしない場合です。
ほとんどのインデックス可能な演算子は厳密で、NULL値の入力に対して真を返すことができませんので、NULLに対してインデックス項目を格納しないことは一見魅力的です。
これはインデックススキャンによって何も返しません。
しかし、最初のインデックス列に対する制限がないインデックススキャンでは、この引数は失敗します。
プランナがこうしたスキャンキーをまったく持たないインデックスを使用することを決定する可能性がありますので、実際これは、amoptionalkeyが真のインデックスはNULLインデックスを持たなければならないことを意味します。
関連する制限として、プランナはこれらの列を制限しない問い合わせでインデックスを使用できることを前提とするため、複数のインデックス列をサポートするインデックスアクセスメソッドは1番目の後の列でNULL値のインデックスをサポートしなければならないということがあります。
例えば、(a,b)に対するインデックスに、WHERE a = 4という条件で問い合わせを行うことを考えてみます。
システムは、このインデックスをa = 4を持つ行をスキャンすることに使用できるものと仮定します。
これはもし、bがNULLの場合の行をインデックスが省略する場合は間違っています。
しかし、最初のインデックス列がNULLの場合に行を省略することは問題ありません
また、NULLをインデックス付けするインデックスアクセスメソッドはamsearchnullsを設定する可能性があります。
これは検索条件としてIS NULLおよびIS NOT NULL句をサポートすることを示します。