SP-GiSTは高度に抽象化されたインタフェースを提供します。アクセスメソッドの開発者は特定のデータ型専用のメソッドだけを開発する必要があります。 SP-GiSTのコアは効率的なディスクマッピングと木構造の探索を担当します。 また、同時実行制御とログ出力も担当します。
SP-GiSTのツリーのリーフタプルは、インデックスの付けられた列の損失のある表現を含むこともできますが、通常はインデックスの付けられた列と同じデータ型の値を含んでいます。 ルートレベルに格納されたリーフタプルは、インデックスが付けられた元のデータの値を直接表現していますが、より下のレベルのリーフタプルは、接尾辞など、部分的な値しか含んでいないかも知れません。 この場合、演算子クラスのサポート関数が、内部タプルをリーフレベルまでたどりながら集める情報を使って元の値を再構築できる必要があります。
SP-GiSTインデックスがINCLUDE
列を付けて作成された場合には、その列の値もリーフタプルに格納されます。
INCLUDE
列はSP-GiST演算子クラスとは関係ありませんので、ここではこれ以上議論しません。
内部タプルは、探索木の分岐点となるため、もっと複雑です。 それぞれの内部タプルは1つ以上のノードの集合を含んでおり、ノードは類似のリーフ値のグループを表現します。 ノードは下向きのリンクを含んでおり、これは下のレベルの別の内部タプルを指すか、あるいはすべて同じインデックスページ上に載っているリーフタプルの短いリストを指しています。 それぞれのノードは、通常、それを記述するラベルを持っています。 例えば、基数木では、ノードのラベルは文字列の値の次の文字にすることができます。 (あるいは、すべての内部タプルについて、決まったノードの集合しか扱わないのであれば、演算子クラスはノードのラベルを省略することができます。 66.4.2を参照してください。) 省略可能ですが、内部タプルはそのすべてのメンバーを記述する接頭辞の値を持つことができます。 基数木では、これは表現される文字列に共通の接頭辞とすることができます。 接頭辞の値は、必ずしも本当の接頭辞である必要はなく、演算子クラスが必要とする任意の値で良いです。 例えば四分木では、その中心点を保持し、4つの象限をそこから相対的に測るようにできます。 そうすると、四分木の内部タプルはこの中心点の周りの象限に対応する4つのノードも含むことになるでしょう。
木構造のアルゴリズムには、現在のタプルのレベル(深さ)を知っていることが必要なものがあります。そこで、SP-GiSTのコアは、演算子クラスが木構造をたどって下がるときにレベル数の管理を可能にしています。 また、必要であれば、表現される値を加算的に再構築すること、また木構造を下る間に追加データ(探索値と呼ばれます)を渡すこともサポートしています。
SP-GiSTのコアのコードはnullエントリについても対応しています。 SP-GiSTのインデックスはインデックス列がnullのエントリについても格納しますが、これはインデックスの演算子クラスのコードからは隠されているので、nullのインデックスエントリや検索条件が演算子クラスのメソッドに渡されることはありません。 (SP-GiSTの演算子は厳格なのでNULL値について成功を返すことはできないと想定されています。) 従って、ここではこれ以上、NULLについて議論しません。
SP-GiSTのインデックス演算子クラスが提供しなければならないユーザ定義メソッドは5つあり、加えて、オプションのメソッドが2つあります。
5つの必須メソッド全ては2つのinternal
引数を受け付けるというしきたりに従い、1つ目はサポートメソッドへの入力値を含むC構造体へのポインタで、一方2つ目は出力値が配置されるであろうC構造体へのポインタです。
4つの必須メソッドでは、その結果がすべて出力構造体の中にあるので、単にvoid
を返します。ですが、leaf_consistent
はboolean
の結果を返します。
メソッドは、その入力構造体のどのフィールドも変更してはいけません。
どんな場合でも、出力構造体はユーザ定義メソッドを呼び出す前にゼロに初期化されます。
オプションの6番目のメソッドcompress
は、唯一の引数としてインデックス付けされるdatum
を受け付け、リーフタプルの物理格納に適した値を返します。
オプションの7番目のメソッドoptions
は、演算子クラスに固有のパラメータを入れるC構造体へのinternal
ポインタを受け付け、void
を返します。
5つの必須ユーザ定義メソッドは以下のとおりです。
config
接頭辞とノードラベルのデータ型のデータ型OIDを含め、インデックスの実装に関する静的情報を返します。
関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_config(internal, internal) RETURNS void ...
1番目の引数はCのspgConfigIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgConfigOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgConfigIn { Oid attType; /* インデックス付けされるデータ型 */ } spgConfigIn; typedef struct spgConfigOut { Oid prefixType; /* 内部タプルの接頭辞のデータ型 */ Oid labelType; /* 内部タプルのノードのラベルのデータ型 */ Oid leafType; /* リーフタプル値のデータ型 */ bool canReturnData; /* 演算子クラスは元のデータを再構築できる */ bool longValuesOK; /* 演算子クラスは1ページより大きな値を扱える */ } spgConfigOut;
attType
は多様のインデックス演算子クラスをサポートするために渡されます。
通常の固定データ型の演算子クラスでは、それは常に同じ値を持っているので無視できます。
接頭辞を使わない演算子クラスでは、prefixType
をVOIDOID
に設定することができます。
同様に、ノードラベルを使わない演算子クラスでは、labelType
をVOIDOID
に設定することができます。
演算子クラスが、元々提供されていたインデックスの値を再構築できるときは、canReturnData
をtrueにします。
attType
が可変長で、演算子クラスが接尾辞付けの繰り返しによって長い値を分割できるときにのみ、longValuesOK
をtrueにします(66.4.1参照)。
leafType
は、演算子クラスのopckeytype
カタログエントリにより定義されたインデックス格納型と一致しなければなりません。
(opckeytype
は0の場合もあり得て、それは格納型が演算子クラスの入力型と同じであることを意味しています。これが最も一般的な状況であることに注意してください。)
後方互換性のため、config
メソッドはleafType
を他の値に設定して、その値を使うことができます。ですが、インデックスの内容がカタログでは誤って特定されますので、これは非推奨です。
また、leafType
を初期化しないまま(0)にできます。これはopckeytype
から導かれたインデックス格納型を意味すると解釈されます。
attType
とleafType
が異なる場合には、オプションのメソッドcompress
を提供しなければなりません。
メソッドcompress
は、インデックス付けされるデータをattType
からleafType
に変換する責任があります。
choose
内部タプルに新しい値を挿入するときのメソッドを選択します。
関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_choose(internal, internal) RETURNS void ...
1番目の引数はCのspgChooseIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgChooseOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgChooseIn { Datum datum; /* インデックス付けされる元のデータ */ Datum leafDatum; /* リーフに保存されている現在のデータ */ int level; /* (0から数えた)現在のレベル */ /* 現在の内部タプルからのデータ */ bool allTheSame; /* タプルはall-the-sameの印を付けられているか */ bool hasPrefix; /* タプルは接頭辞を持っているか */ Datum prefixDatum; /* そうであれば、接頭辞の値 */ int nNodes; /* 内部タプル内のノード数 */ Datum *nodeLabels; /* ノードのラベルの値(なければNULL) */ } spgChooseIn; typedef enum spgChooseResultType { spgMatchNode = 1, /* 既存のノードに下がる */ spgAddNode, /* ノードに内部タプルを追加する */ spgSplitTuple /* 内部タプルを分割する(その接頭辞を変更する) */ } spgChooseResultType; typedef struct spgChooseOut { spgChooseResultType resultType; /* アクションコード、上記参照 */ union { struct /* spgMatchNodeの結果 */ { int nodeN; /* このノードに下がる(0からのインデックス) */ int levelAdd; /* この分だけレベルを増やす */ Datum restDatum; /* 新しいリーフデータ */ } matchNode; struct /* spgAddNodeの結果 */ { Datum nodeLabel; /* 新しいノードのラベル */ int nodeN; /* 挿入する場所(0からのインデックス) */ } addNode; struct /* spgSplitTupleの結果 */ { /* 子タプルを1つ持つ新しい上位のレベルの内部タプルを生成するための情報 */ bool prefixHasPrefix; /* タプルは接頭辞を持つか */ Datum prefixPrefixDatum; /* そうならば、その値 */ int prefixNNodes; /* ノード数 */ Datum *prefixNodeLabels; /* そのラベル(ラベルがなければNULL) */ int childNodeN; /* どのタプルが子タプルを得るか */ /* 古いノードをすべて持つ新しい低位の内部タプルを生成するための情報 */ bool postfixHasPrefix; /* タプルは接頭辞を持つか */ Datum postfixPrefixDatum; /* そうならば、その値 */ } splitTuple; } result; } spgChooseOut;
datum
はインデックスに挿入できたspgConfigIn
.attType
型の元データです。
leafDatum
はspgConfigOut
.leafType
型の値です。これは最初は、メソッドcompress
が提供されているならdatum
に適用されたメソッドcompress
の結果で、さもなくばdatum
と同じ値です。
leafDatum
は、choose
やpicksplit
メソッドがこれを変更すると、ツリーのより低いレベルで変化することがあります。
挿入の探索がリーフページに到達するとき、leafDatum
の現在値は、新しく作成されるリーフタプルに格納される値です。
level
は、ルートレベルを0として、現在の内部タプルのレベルを示します。
現在の内部タプルが複数の同等なノードを含むとして印を付けられているとき、allTheSame
をtrueにします(66.4.3参照)。
現在の内部タプルが接頭辞を含むとき、hasPrefix
をtrueにします。
このとき、prefixDatum
がその値になります。
nNodes
は内部タプルが含む子ノードの数で、nodeLabels
はそれらのラベル値の配列、あるいはラベルがなければNULLになります。
choose
関数は、新しい値が既存の子ノードの1つとマッチするか、新しい子ノードを追加する必要があるか、あるいは新しい値がタプルの接頭辞と適合しないので内部タプルを分割してより制限のない接頭辞を作成する必要があるか、を決定することができます。
新しい値が既存の子ノードの1つにマッチしたときは、resultType
をspgMatchNode
にセットします。
nodeN
はノードの配列中のそのノードの(0からの)番号にセットします。
levelAdd
は、そのノードをたどって下がるときに生じたlevel
の増分にセットします。あるいは演算子クラスがレベルを使っていなければ0のままにします。
restDatum
は、演算子クラスがデータをあるレベルから次のレベルに変更しないのであれば、datum
に等しくセットします。そうでなければ、次のレベルでleafDatum
として使われる修正された値にセットします。
新しい子ノードを追加しなければならないときは、resultType
をspgAddNode
にセットします。
nodeLabel
は、新しいノードで使われるラベルにセットし、nodeN
はノードの配列中の挿入される場所のノードの(0からの)番号にセットします。
ノードを追加した後で、choose
関数を修正された内部タプルを使って再び呼び出しますが、このときは、spgMatchNode
という結果になるはずです。
新しい値がタプルの接頭辞と適合しないときは、resultType
をspgSplitTuple
にセットします。
このアクションは、すべての既存のノードを新しい低位の内部タプルに移動し、新しい低位の内部タプルを指す単一の下向きのリンクを持つ新しいタプルで既存のタプルを置換します。
prefixHasPrefix
は新しい上位のタプルが接頭辞を持つかどうかを示し、持つ場合にはprefixPrefixDatum
をその接頭辞の値にセットします。
インデックスに追加される新しい値を受け入れるため、新しい接頭辞の値は元のものよりも十分に制限の緩いものになっていなければなりません。
prefixNNodes
は新しいタプルで必要なノード数にセットし、prefixNodeLabels
はラベルを保持するためにpallocされた配列に、ノードのラベルが必要でないときはNULLにセットします。
新しい上位のタプルの全サイズは置き換えるタプルの全サイズよりも大きくはないことに注意してください。これは新しい接頭辞と新しいラベルの長さを制約します。
childNodeN
は、新しい低位の内部タプルへ下向きにリンクするノードの(0からの)番号にセットします。
postfixHasPrefix
は、新しい低位のタプルが接頭辞を持つかどうかを示し、持つときにはpostfixPrefixDatum
を接頭辞の値にセットします。
新しい低位に移動したタプルのノードのラベルを変更する機会も、子のインデックスのエントリを変更する機会もありませんから、これら2つの接頭辞と(もしあれば)下向きのリンクのノードのラベルの組み合わせは、元の接頭辞と同じ意味を持つ必要があります。
ノードが分割された後で、置換した内部タプルを使ってchoose
関数を再び呼び出します。
この呼び出しは、spgSplitTuple
アクションにより適切なノードが作られなければ、spgAddNode
という結果になります。
そのうち、choose
がspgMatchNode
を返し、次のレベルに下がる挿入が可能となります。
picksplit
リーフタプルの集合に対し、新しい内部タプルをどうやって作るかを決定します。
関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ...
1番目の引数はCのspgPickSplitIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgPickSplitOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgPickSplitIn { int nTuples; /* リーフタプルの数 */ Datum *datums; /* そのデータ(長さnTuplesの配列) */ int level; /* (0から数えた)現在のレベル */ } spgPickSplitIn; typedef struct spgPickSplitOut { bool hasPrefix; /* 新しい内部タプルは接頭辞を持つか */ Datum prefixDatum; /* もしそうなら、その値 */ int nNodes; /* 新しい内部タプルのノード数 */ Datum *nodeLabels; /* そのラベル(ラベルがなければNULL) */ int *mapTuplesToNodes; /* 各リーフタプルへのノードのインデックス */ Datum *leafTupleDatums; /* 新しい各リーフタプルに保存されているデータ */ } spgPickSplitOut;
nTuples
は与えられるリーフタプルの個数です。
datums
はspgConfigOut
.leafType
型のそれらのデータ値の配列です。
level
はすべてのリーフタプルが共有する現在のレベルで、これが新しい内部タプルのレベルになります。
hasPrefix
は新しい内部タプルが接頭辞を持つかどうかを示し、持つ場合はprefixDatum
を接頭辞の値にセットします。
nNodes
は新しい内部タプルが含むノードの数を示し、nodeLabels
はそのラベル値の配列に、ノードのラベルが必要でないときはNULLにセットします。
mapTuplesToNodes
は、それぞれのリーフタプルが割り当てられるノードの(0からの)番号の配列にセットします。
leafTupleDatums
は新しいリーフタプルに格納される値の配列にセットします(演算子クラスがデータをあるレベルから次のレベルに変更しなければこれらは入力のdatums
と同じになります)。
picksplit
関数は、nodeLabels
、mapTuplesToNodes
、leafTupleDatums
の配列についてpallocしなければならないことに注意してください。
2つ以上のリーフタプルを与えた場合、picksplit
関数はそれらを2つ以上のノードに分類すると予想されます。そうでなければ、リーフタプルを複数のページにまたがって分割するという、この操作の究極の目的を実現できないからです。
従って、picksplit
がすべてのリーフタプルを同じノードに置くことになった場合には、SP-GiSTのコアのコードがその決定を覆して内部タプルを生成し、その中の複数の同一のラベルが付けられたノードに、リーフタプルが無作為に割り当てられます。
そのようなタプルは、このことが発生したことを明示するため、allTheSame
と印がつけられます。
choose
関数とinner_consistent
関数は、これらの内部タプルについて、適切な注意をして取り扱わなければなりません。
詳細な情報は66.4.3を参照してください。
config
関数がlongValuesOK
をtrueにセットし、1ページよりも大きな入力値を与える場合にのみ、picksplit
を1つだけのリーフタプルに適用できます。
この場合の操作の重要な点は、接頭辞をはがして、新しい、より短いリーフデータの値を生成することです。
この呼出は、1ページに収まる短さのリーフデータが生成されるまで繰り返されます。
詳細な情報は66.4.1を参照してください。
inner_consistent
ツリーの探索でたどるべきノード(枝)の集合を返します。
関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...
1番目の引数はCのspgInnerConsistentIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgInnerConsistentOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgInnerConsistentIn { ScanKey scankeys; /* 演算子と比較する値の配列 */ ScanKey orderbys; /* 順序付け演算子と比較する値の配列 */ int nkeys; /* scankeys配列の長さ */ int norderbys; /* orderbys配列の長さ */ Datum reconstructedValue; /* 親で再構築された値 */ void *traversalValue; /* 演算子クラスに固有の探索値 */ MemoryContext traversalMemoryContext; /* 新しい探索値をここに入れる */ int level; /* (0から数えた)現在のレベル */ bool returnData; /* 元のデータを返さなければならないか */ /* 現在の内部タプルからのデータ */ bool allTheSame; /* タプルはall-the-sameと印が付けられているか */ bool hasPrefix; /* タプルは接頭辞を持つか */ Datum prefixDatum; /* もしそうなら、接頭辞の値 */ int nNodes; /* 内部タプルの中のノード数 */ Datum *nodeLabels; /* ノードのラベルの値(なければNULL) */ } spgInnerConsistentIn; typedef struct spgInnerConsistentOut { int nNodes; /* 訪れるべき子ノードの数 */ int *nodeNumbers; /* ノードの配列でのそのインデックス */ int *levelAdds; /* この分だけそれぞれレベルを挙げる */ Datum *reconstructedValues; /* 関連する再構築された値 */ void **traversalValues; /* 演算子クラスに固有の探索値 */ double **distances; /* 関連する距離 */ } spgInnerConsistentOut;
配列scankeys
は長さがnkeys
で、インデックス検索の条件を記述します。
複数の条件はANDで結合されます。つまり、条件のすべてを満たすインデックスエントリのみが対象となります。
(nkeys
= 0 は全インデックスエントリが問い合わせを満たす意味になる、ということに注意してください。)
通常、consistent関数では、配列のそれぞれのエントリのsk_strategy
およびsk_argument
フィールドのみが問題となります。これらのフィールドにはそれぞれインデックス付け可能な演算子と比較値が入ります。
とりわけ、比較値がNULLかどうかを確認するためにsk_flags
を検査する必要はありません。なぜならSP-GiSTのコアのコードがそのような条件を除外するからです。
配列orderbys
は長さがnorderbys
で、(もしあれば)順序付け演算子を同じように記述します。
reconstructedValue
は親タプルのために再構築された値で、ルートレベルの場合、あるいは親レベルのinner_consistent
関数が値を返さなかった場合は(Datum) 0
となります。
traversalValue
は親インデックスのタプルのinner_consistent
の前の呼び出しから渡された探索データへのポインタで、ルートレベルならNULLです。
traversalMemoryContext
は出力探索値が格納されるメモリコンテキストです(以下を参照)。
level
は現在の内部タプルのレベルを、ルートレベルを0として数えたものです。
returnData
は、この問い合わせで再構築されたデータが必要な場合にtrue
となりますが、これはconfig
関数がcanReturnData
であると主張した場合にのみ、そうなります。
現在の内部タプルが「all-the-same」と印付けされているなら、allTheSame
は真になります。この場合、(もしあるなら)全てのノードが同じラベルを持ち、問い合わせに全てが一致するか、全く一致しないかのいずれかになります(66.4.3を参照)。
現在の内部タプルがプレフィックスを含んでいるならhasPrefix
は真になります。その場合、prefixDatum
がその値です。
nNodes
は内部タプルに含まれる子ノードの数で、nodeLabels
はそれらのラベル値の配列、あるいは、ノードがラベルを持たないならNULLです。
nNodes
は探索で訪れる必要のある子ノードの数にセットされなければなりません。また、nodeNumbers
はそれらの番号の配列にセットされなければなりません。
演算子クラスがレベルを監視しているときは、それぞれのノードへと下って訪れるときに必要なレベルの増分の配列をlevelAdds
にセットします。
(この増分はすべてのノードについて同じになることも多いですが、必ずしもそうなるとは限らないので配列が使われます。)
値の再構築が必要なときには、訪れるそれぞれの子ノードについて再構築された値の配列をreconstructedValues
にセットします。再構築が必要でなければ、reconstructedValues
をNULLのままにします。
再構築された値はspgConfigOut
.leafType
型と仮定されます。
(しかしながら、コアシステムはそれらに対してコピー以外のことをしませんので、leafType
と同じtyplen
とtypbyval
属性を持っていれば十分です。)
順序付け検索を実行するなら、orderbys
配列に従ってdistances
に距離の値の配列を設定します(距離の最も近いノードが最初に処理されます)。
そうでなければNULLのままにします。
追加の外部情報(「探索値」)をツリー探索の下位レベルに渡したい場合は、traversalValues
を適切な探索値、訪れるそれぞれの子ノードについて1つの配列にセットします。
それ以外の場合はtraversalValues
をNULLのままにします。
inner_consistent
関数は、現在のメモリコンテキスト内のnodeNumbers
、levelAdds
、distances
、reconstructedValues
、traversalValues
の配列についてpallocしなければならないことに注意してください。
ただし、traversalValues
配列が指すすべての出力探索値はtraversalMemoryContext
内に割り当てられます。
それぞれの探索値は1つのpallocされた塊でなければなりません。
leaf_consistent
リーフタプルが問い合わせを満たす場合、trueを返します。
関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...
1番目の引数はCのspgLeafConsistentIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgLeafConsistentOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgLeafConsistentIn { ScanKey scankeys; /* 演算子と比較する値の配列 */ ScanKey orderbys; /* 順序付け演算子と比較する値の配列 */ int nkeys; /* scankeys配列の長さ */ int norderbys; /* orderbys配列の長さ */ Datum reconstructedValue; /* 親で再構築された値 */ void *traversalValue; /* 演算子クラスに固有の探索値 */ int level; /* (0から数えた)現在のレベル */ bool returnData; /* 元のデータを返さなければならないか */ Datum leafDatum; /* リーフタプルのデータ */ } spgLeafConsistentIn; typedef struct spgLeafConsistentOut { Datum leafValue; /* もしあれば、再構築された元のデータ */ bool recheck; /* 演算子を再チェックする必要があればtrue */ bool recheckDistances; /* 距離を再チェックする必要があればtrue */ double *distances; /* 関連する距離 */ } spgLeafConsistentOut;
配列scankeys
は長さがnkeys
で、インデックス探索の条件を記述します。
複数の条件はANDで結合されます。つまり、条件のすべてを満たすインデックスエントリのみが対象となります。
(nkeys
が0ならば、すべてのエントリが検索条件を満たすことになる、ということに注意してください。)
通常、consistent関数では、配列のそれぞれのエントリのsk_strategy
およびsk_argument
フィールドのみが問題となります。これらのフィールドにはそれぞれインデックス付け可能な演算子と比較値が入ります。
なお、比較値がNULLかどうかを確認するためにsk_flags
を検査する必要はありません。なぜならSP-GiSTのコアのコードがそのような条件を除外するからです。
配列orderbys
は長さがnorderbys
で、順序付け演算子を同じように記述します。
reconstructedValue
は親タプルのために再構築された値で、ルートレベルの場合、あるいは親レベルのinner_consistent
関数が値を返さなかった場合は(Datum) 0
となります。
traversalValue
は親インデックスのタプルのinner_consistent
の前の呼び出しから渡された探索データへのポインタで、ルートレベルならNULLです。
level
は現在のリーフタプルのレベルを、ルートレベルを0として数えたものです。
returnData
は、この問い合わせで再構築されたデータが必要な場合にtrue
となりますが、これはconfig
関数がcanReturnData
を確認した場合にのみ、そうなります。
leafDatum
は現在のリーフタプルに格納されているspgConfigOut
.leafType
のキーの値です。
この関数は、リーフタプルが問い合わせにマッチすればtrue
を返し、マッチしなければfalse
を返します。
true
の場合、returnData
がtrue
であれば、leafValue
を、このリーフタプルにインデックス付けするために元々提供された(spgConfigIn
.attType
型の)値に設定しなければなりません。
また、マッチするかどうかが不確実で、マッチするかの確認のために実際のヒープタプルに演算子を再適用しなければならないときは、recheck
がtrue
にセットされることがあります。
順序付け検索を実行するなら、orderbys
配列に従ってdistances
に距離の値の配列を設定します。
そうでなければNULLのままにします。
返される距離の少なくとも1つが正確でないのなら、recheckDistances
にtrueを設定します。
この場合、エクゼキュータはヒープからタプルを取得した後正確な距離を計算し、必要ならタプルを並べ替えます。
オプションのユーザ定義メソッドは以下です。
Datum compress(Datum in)
インデックスのリーフタプルでデータ項目を物理ストレージに適した形式に変換します。
spgConfigIn
.attType
型の値を受け付け、spgConfigOut
.leafType
型の値を返します。
出力値は行に収まらないTOASTを含んでいてはいけません。
注意: compress
メソッドは格納される値にのみ適用されます。
適合するメソッドはcompress
を使って変換することなく、問い合わせのscankeys
をそのまま受け取ります。
options
演算子クラスの振舞いを制御するユーザに可視のパラメータの集合を定義します。
関数のSQL宣言は以下のようになります。
CREATE OR REPLACE FUNCTION my_options(internal) RETURNS void AS 'MODULE_PATHNAME' LANGUAGE C STRICT;
関数にはlocal_relopts
構造体へのポインタが渡されますが、構造体を演算子クラスに固有のオプションの集合で満たすことが必要です。
オプションはマクロPG_HAS_OPCLASS_OPTIONS()
とPG_GET_OPCLASS_OPTIONS()
を使って他のサポート関数からアクセスできます。
SP-GiSTでのキーの表現には柔軟性がありますので、ユーザに固有のパラメータに依存するかもしれません。
SP-GiSTのすべてのサポートメソッドは、通常は短期間有効なメモリコンテキスト内で呼び出されます。つまり、それぞれのタプルについて処理した後でCurrentMemoryContext
はリセットされます。
したがって、pallocしたものすべてについてpfreeすることを気にかけることはあまり重要ではありません。
(config
メソッドは例外で、メモリリークを避けるようにする必要があります。
しかし、通常はconfig
メソッドは、パラメータとして渡された構造体に定数を代入する以外、何もする必要がありません。)
インデックス付けされた列が照合可能なデータ型の場合、インデックスの照合は、標準的なPG_GET_COLLATION()
の仕組みを使ってすべてのサポートメソッドに渡されます。