<acronym>SP-GiST</acronym> is an abbreviation for space-partitioned <acronym>GiST</acronym>. <acronym>SP-GiST</acronym> supports partitioned search trees, which facilitate development of a wide range of different non-balanced data structures, such as quad-trees, k-d trees, and radix trees (tries). The common feature of these structures is that they repeatedly divide the search space into partitions that need not be of equal size. Searches that are well matched to the partitioning rule can be very fast. SP-GiSTは、空間分割された(Space-Partitioned)GiSTを短縮した語です。 SP-GiSTは分割された探索木をサポートし、四分木、kd木、基数木(トライ木)など広範にわたる様々な非平衡データ構造の開発を可能にします。 これらの構造に共通の特徴は、それらが探索空間を繰り返し小さな領域に分割し、その領域の大きさが必ずしも等しくない、ということです。 分割規則によく適合した検索は非常に高速になります。
These popular data structures were originally developed for in-memory usage. In main memory, they are usually designed as a set of dynamically allocated nodes linked by pointers. This is not suitable for direct storing on disk, since these chains of pointers can be rather long which would require too many disk accesses. In contrast, disk-based data structures should have a high fanout to minimize I/O. The challenge addressed by <acronym>SP-GiST</acronym> is to map search tree nodes to disk pages in such a way that a search need access only a few disk pages, even if it traverses many nodes. これらのよく使われるデータ構造は、元々はメモリ内での利用のために開発されたものでした。 主記憶上では、それらは通常、ポインタにより接続され、動的に割り当てられるノードの集合として設計されます。 このようなポインタのチェーンは長くなりがちで、非常に多くのディスクアクセスが必要となるため、ディスク上に直接格納するには適しません。 これとは反対に、ディスクベースのデータ構造は、I/Oを最小化する、大きな論理出力数を持つべきです。 SP-GiSTによって解決される困難とは、探索木のノードをディスクのページにマップするときに、多数のノードを通り抜ける場合であっても、探索ではごく少数のディスクページにしかアクセスしないですむようにすることです。
Like <acronym>GiST</acronym>, <acronym>SP-GiST</acronym> is meant to allow the development of custom data types with the appropriate access methods, by an expert in the domain of the data type, rather than a database expert. GiSTと同じく、SP-GiSTは適切なアクセス方法のある独自のデータ型の開発を可能にするためのもので、データベースのエキスパートよりもむしろ、そのデータ型の領域のエキスパートによる開発を可能にします。
Some of the information here is derived from Purdue University's SP-GiST Indexing Project <ulink url="https://www.cs.purdue.edu/spgist/">web site</ulink>. The <acronym>SP-GiST</acronym> implementation in <productname>PostgreSQL</productname> is primarily maintained by Teodor Sigaev and Oleg Bartunov, and there is more information on their <!-- URL will be changed --> <ulink url="http://www.sai.msu.su/~megera/wiki/spgist_dev">web site</ulink>. ここで記述する情報の一部はPurdue大学のSP-GiSTインデックスプロジェクトWEBサイトによるものです。 PostgreSQLでのSP-GiSTの実装は、おもにTeodor SigaevとOleg Bartunovによって保守されており、詳しい情報は彼らのWEBサイトにあります。
The core <productname>PostgreSQL</productname> distribution includes the <acronym>SP-GiST</acronym> operator classes shown in <xref linkend="spgist-builtin-opclasses-table"/>. PostgreSQLのコアディストリビューションには表 64.2に示されるSP-GiSTの演算子クラスが含まれます。
表64.2 組み込みSP-GiST演算子クラス
名前 | インデックス可能な演算子 | 順序付け演算子 |
---|---|---|
box_ops | << (box,box) | <-> (box,point) |
&< (box,box) | ||
&> (box,box) | ||
>> (box,box) | ||
<@ (box,box) | ||
@> (box,box) | ||
~= (box,box) | ||
&& (box,box) | ||
<<| (box,box) | ||
&<| (box,box) | ||
|&> (box,box) | ||
|>> (box,box) | ||
inet_ops | << (inet,inet) | |
<<= (inet,inet) | ||
>> (inet,inet) | ||
>>= (inet,inet) | ||
= (inet,inet) | ||
<> (inet,inet) | ||
< (inet,inet) | ||
<= (inet,inet) | ||
> (inet,inet) | ||
>= (inet,inet) | ||
&& (inet,inet) | ||
kd_point_ops | |>> (point,point) | <-> (point,point) |
<< (point,point) | ||
>> (point,point) | ||
<<| (point,point) | ||
~= (point,point) | ||
<@ (point,box) | ||
poly_ops | << (polygon,polygon) | <-> (polygon,point) |
&< (polygon,polygon) | ||
&> (polygon,polygon) | ||
>> (polygon,polygon) | ||
<@ (polygon,polygon) | ||
@> (polygon,polygon) | ||
~= (polygon,polygon) | ||
&& (polygon,polygon) | ||
<<| (polygon,polygon) | ||
&<| (polygon,polygon) | ||
|>> (polygon,polygon) | ||
|&> (polygon,polygon) | ||
quad_point_ops | |>> (point,point) | <-> (point,point) |
<< (point,point) | ||
>> (point,point) | ||
<<| (point,point) | ||
~= (point,point) | ||
<@ (point,box) | ||
range_ops | = (anyrange,anyrange) | |
&& (anyrange,anyrange) | ||
@> (anyrange,anyelement) | ||
@> (anyrange,anyrange) | ||
<@ (anyrange,anyrange) | ||
<< (anyrange,anyrange) | ||
>> (anyrange,anyrange) | ||
&< (anyrange,anyrange) | ||
&> (anyrange,anyrange) | ||
-|- (anyrange,anyrange) | ||
text_ops | = (text,text) | |
< (text,text) | ||
<= (text,text) | ||
> (text,text) | ||
>= (text,text) | ||
~<~ (text,text) | ||
~<=~ (text,text) | ||
~>=~ (text,text) | ||
~>~ (text,text) | ||
^@ (text,text) |
Of the two operator classes for type <type>point</type>,
<literal>quad_point_ops</literal> is the default. <literal>kd_point_ops</literal>
supports the same operators but uses a different index data structure that
may offer better performance in some applications.
point
型の2つの演算子クラスのうち、quad_point_ops
がデフォルトです。
kd_point_ops
は同じ演算子をサポートしますが、異なるインデックスデータ構造を使うため、アプリケーションによってはより良いパフォーマンスを提供することがあります。
The <literal>quad_point_ops</literal>, <literal>kd_point_ops</literal> and
<literal>poly_ops</literal> operator classes support the <literal><-></literal>
ordering operator, which enables the k-nearest neighbor (<literal>k-NN</literal>)
search over indexed point or polygon data sets.
quad_point_ops
、kd_point_ops
、poly_ops
演算子クラスは<->
順序付け演算子をサポートしますので、インデックス付けされた点や多角形のデータ集合に対してk近傍(k-NN
)探索が可能です。
<acronym>SP-GiST</acronym> offers an interface with a high level of abstraction, requiring the access method developer to implement only methods specific to a given data type. The <acronym>SP-GiST</acronym> core is responsible for efficient disk mapping and searching the tree structure. It also takes care of concurrency and logging considerations. SP-GiSTは高度に抽象化されたインタフェースを提供します。アクセスメソッドの開発者は特定のデータ型専用のメソッドだけを開発する必要があります。 SP-GiSTのコアは効率的なディスクマッピングと木構造の探索を担当します。 また、同時実行制御とログ出力も担当します。
Leaf tuples of an <acronym>SP-GiST</acronym> tree usually contain values of the same data type as the indexed column, although it is also possible for them to contain lossy representations of the indexed column. Leaf tuples stored at the root level will directly represent the original indexed data value, but leaf tuples at lower levels might contain only a partial value, such as a suffix. In that case the operator class support functions must be able to reconstruct the original value using information accumulated from the inner tuples that are passed through to reach the leaf level. SP-GiSTのツリーのリーフタプルは、インデックスの付けられた列の損失のある表現を含むこともできますが、通常はインデックスの付けられた列と同じデータ型の値を含んでいます。 ルートレベルに格納されたリーフタプルは、インデックスが付けられた元のデータの値を直接表現していますが、より下のレベルのリーフタプルは、接尾辞など、部分的な値しか含んでいないかも知れません。 この場合、演算子クラスのサポート関数が、内部タプルをリーフレベルまでたどりながら集める情報を使って元の値を再構築できる必要があります。
When an <acronym>SP-GiST</acronym> index is created with
<literal>INCLUDE</literal> columns, the values of those columns are also
stored in leaf tuples. The <literal>INCLUDE</literal> columns are of no
concern to the <acronym>SP-GiST</acronym> operator class, so they are
not discussed further here.
SP-GiSTインデックスがINCLUDE
列を付けて作成された場合には、その列の値もリーフタプルに格納されます。
INCLUDE
列はSP-GiST演算子クラスとは関係ありませんので、ここではこれ以上説明しません。
Inner tuples are more complex, since they are branching points in the search tree. Each inner tuple contains a set of one or more <firstterm>nodes</firstterm>, which represent groups of similar leaf values. A node contains a downlink that leads either to another, lower-level inner tuple, or to a short list of leaf tuples that all lie on the same index page. Each node normally has a <firstterm>label</firstterm> that describes it; for example, in a radix tree the node label could be the next character of the string value. (Alternatively, an operator class can omit the node labels, if it works with a fixed set of nodes for all inner tuples; see <xref linkend="spgist-null-labels"/>.) Optionally, an inner tuple can have a <firstterm>prefix</firstterm> value that describes all its members. In a radix tree this could be the common prefix of the represented strings. The prefix value is not necessarily really a prefix, but can be any data needed by the operator class; for example, in a quad-tree it can store the central point that the four quadrants are measured with respect to. A quad-tree inner tuple would then also contain four nodes corresponding to the quadrants around this central point. 内部タプルは、探索木の分岐点となるため、もっと複雑です。 それぞれの内部タプルは1つ以上のノードの集合を含んでおり、ノードは類似のリーフ値のグループを表現します。 ノードは下向きのリンクを含んでおり、これは下のレベルの別の内部タプルを指すか、あるいはすべて同じインデックスページ上に載っているリーフタプルの短いリストを指しています。 それぞれのノードは、通常、それを記述するラベルを持っています。 例えば、基数木では、ノードのラベルは文字列の値の次の文字にすることができます。 (あるいは、すべての内部タプルについて、決まったノードの集合しか扱わないのであれば、演算子クラスはノードのラベルを省略することができます。 64.3.4.2を参照してください。) 省略可能ですが、内部タプルはそのすべてのメンバを記述する接頭辞の値を持つことができます。 基数木では、これは表現される文字列に共通の接頭辞とすることができます。 接頭辞の値は、必ずしも本当の接頭辞である必要はなく、演算子クラスが必要とする任意の値で良いです。 例えば四分木では、その中心点を保持し、4つの象限をそこから相対的に測るようにできます。 そうすると、四分木の内部タプルはこの中心点の周りの象限に対応する4つのノードも含むことになるでしょう。
Some tree algorithms require knowledge of level (or depth) of the current tuple, so the <acronym>SP-GiST</acronym> core provides the possibility for operator classes to manage level counting while descending the tree. There is also support for incrementally reconstructing the represented value when that is needed, and for passing down additional data (called <firstterm>traverse values</firstterm>) during a tree descent. 木構造のアルゴリズムには、現在のタプルのレベル(深さ)を知っていることが必要なものがあります。そこで、SP-GiSTのコアは、演算子クラスが木構造をたどって下がるときにレベル数の管理を可能にしています。 また、必要であれば、表現される値を加算的に再構築すること、また木構造を下る間に追加データ(探索値と呼ばれます)を渡すこともサポートしています。
The <acronym>SP-GiST</acronym> core code takes care of null entries. Although <acronym>SP-GiST</acronym> indexes do store entries for nulls in indexed columns, this is hidden from the index operator class code: no null index entries or search conditions will ever be passed to the operator class methods. (It is assumed that <acronym>SP-GiST</acronym> operators are strict and so cannot succeed for null values.) Null values are therefore not discussed further here. SP-GiSTのコアのコードはnullエントリについても対応しています。 SP-GiSTのインデックスはインデックス列がnullのエントリについても格納しますが、これはインデックスの演算子クラスのコードからは隠されているので、nullのインデックスエントリや検索条件が演算子クラスのメソッドに渡されることはありません。 (SP-GiSTの演算子は厳格なのでNULL値について成功を返すことはできないと想定されています。) 従って、ここではこれ以上、NULLについて説明しません。
There are five user-defined methods that an index operator class for
<acronym>SP-GiST</acronym> must provide, and two are optional. All five
mandatory methods follow the convention of accepting two <type>internal</type>
arguments, the first of which is a pointer to a C struct containing input
values for the support method, while the second argument is a pointer to a
C struct where output values must be placed. Four of the mandatory methods just
return <type>void</type>, since all their results appear in the output struct; but
<function>leaf_consistent</function> returns a <type>boolean</type> result.
The methods must not modify any fields of their input structs. In all
cases, the output struct is initialized to zeroes before calling the
user-defined method. The optional sixth method <function>compress</function>
accepts a <type>datum</type> to be indexed as the only argument and returns a value suitable
for physical storage in a leaf tuple. The optional seventh method
<function>options</function> accepts an <type>internal</type> pointer to a C struct, where
opclass-specific parameters should be placed, and returns <type>void</type>.
SP-GiSTのインデックス演算子クラスが提供しなければならないユーザ定義メソッドは5つあり、加えて、オプションのメソッドが2つあります。
5つの必須メソッド全ては2つのinternal
引数を受け付けるというしきたりに従い、1つ目はサポートメソッドへの入力値を含むC構造体へのポインタで、一方2つ目は出力値が配置されるであろうC構造体へのポインタです。
4つの必須メソッドでは、その結果がすべて出力構造体の中にあるので、単にvoid
を返します。ですが、leaf_consistent
はboolean
の結果を返します。
メソッドは、その入力構造体のどのフィールドも変更してはいけません。
どんな場合でも、出力構造体はユーザ定義メソッドを呼び出す前にゼロに初期化されます。
オプションの6番目のメソッドcompress
は、唯一の引数としてインデックス付けされるdatum
を受け付け、リーフタプルの物理格納に適した値を返します。
オプションの7番目のメソッドoptions
は、演算子クラスに固有のパラメータを入れるC構造体へのinternal
ポインタを受け付け、void
を返します。
The five mandatory user-defined methods are: 5つの必須ユーザ定義メソッドは以下のとおりです。
config
Returns static information about the index implementation, including the data type OIDs of the prefix and node label data types. 接頭辞とノードラベルのデータ型のデータ型OIDを含め、インデックスの実装に関する静的情報を返します。
The <acronym>SQL</acronym> declaration of the function must look like this: この関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_config(internal, internal) RETURNS void ...
The first argument is a pointer to a <structname>spgConfigIn</structname>
C struct, containing input data for the function.
The second argument is a pointer to a <structname>spgConfigOut</structname>
C struct, which the function must fill with result data.
1番目の引数はCのspgConfigIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgConfigOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgConfigIn { Oid attType; /* Data type to be indexed */ Oid attType; /* インデックス付けされるデータ型 */ } spgConfigIn; typedef struct spgConfigOut { Oid prefixType; /* Data type of inner-tuple prefixes */ Oid labelType; /* Data type of inner-tuple node labels */ Oid leafType; /* Data type of leaf-tuple values */ bool canReturnData; /* Opclass can reconstruct original data */ bool longValuesOK; /* Opclass can cope with values > 1 page */ Oid prefixType; /* 内部タプルの接頭辞のデータ型 */ Oid labelType; /* 内部タプルのノードのラベルのデータ型 */ Oid leafType; /* リーフタプル値のデータ型 */ bool canReturnData; /* 演算子クラスは元のデータを再構築できる */ bool longValuesOK; /* 演算子クラスは1ページより大きな値を扱える */ } spgConfigOut;
<structfield>attType</structfield> is passed in order to support polymorphic
index operator classes; for ordinary fixed-data-type operator classes, it
will always have the same value and so can be ignored.
attType
は多様のインデックス演算子クラスをサポートするために渡されます。
通常の固定データ型の演算子クラスでは、それは常に同じ値を持っているので無視できます。
For operator classes that do not use prefixes,
<structfield>prefixType</structfield> can be set to <literal>VOIDOID</literal>.
Likewise, for operator classes that do not use node labels,
<structfield>labelType</structfield> can be set to <literal>VOIDOID</literal>.
<structfield>canReturnData</structfield> should be set true if the operator class
is capable of reconstructing the originally-supplied index value.
<structfield>longValuesOK</structfield> should be set true only when the
<structfield>attType</structfield> is of variable length and the operator
class is capable of segmenting long values by repeated suffixing
(see <xref linkend="spgist-limits"/>).
接頭辞を使わない演算子クラスでは、prefixType
をVOIDOID
に設定することができます。
同様に、ノードラベルを使わない演算子クラスでは、labelType
をVOIDOID
に設定することができます。
演算子クラスが、元々提供されていたインデックスの値を再構築できるときは、canReturnData
をtrueにします。
attType
が可変長で、演算子クラスが接尾辞付けの繰り返しによって長い値を分割できるときにのみ、longValuesOK
をtrueにします(64.3.4.1参照)。
<structfield>leafType</structfield> should match the index storage type
defined by the operator class's <structfield>opckeytype</structfield>
catalog entry.
(Note that <structfield>opckeytype</structfield> can be zero,
implying the storage type is the same as the operator class's input
type, which is the most common situation.)
For reasons of backward compatibility, the <function>config</function>
method can set <structfield>leafType</structfield> to some other value,
and that value will be used; but this is deprecated since the index
contents are then incorrectly identified in the catalogs.
Also, it's permissible to
leave <structfield>leafType</structfield> uninitialized (zero);
that is interpreted as meaning the index storage type derived from
<structfield>opckeytype</structfield>.
leafType
は、演算子クラスのopckeytype
カタログエントリにより定義されたインデックス格納型と一致しなければなりません。
(opckeytype
は0の場合もあり得て、それは格納型が演算子クラスの入力型と同じであることを意味しています。これが最も一般的な状況であることに注意してください。)
後方互換性のため、config
メソッドはleafType
を他の値に設定して、その値を使うことができます。ですが、インデックスの内容がカタログでは誤って特定されますので、これは非推奨です。
また、leafType
を初期化しないまま(0)にできます。これはopckeytype
から導かれたインデックス格納型を意味すると解釈されます。
When <structfield>attType</structfield>
and <structfield>leafType</structfield> are different, the optional
method <function>compress</function> must be provided.
Method <function>compress</function> is responsible
for transformation of datums to be indexed from <structfield>attType</structfield>
to <structfield>leafType</structfield>.
attType
とleafType
が異なる場合には、オプションのメソッドcompress
を提供しなければなりません。
メソッドcompress
は、インデックス付けされるデータをattType
からleafType
に変換する責任があります。
choose
Chooses a method for inserting a new value into an inner tuple. 内部タプルに新しい値を挿入するときのメソッドを選択します。
The <acronym>SQL</acronym> declaration of the function must look like this: この関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_choose(internal, internal) RETURNS void ...
The first argument is a pointer to a <structname>spgChooseIn</structname>
C struct, containing input data for the function.
The second argument is a pointer to a <structname>spgChooseOut</structname>
C struct, which the function must fill with result data.
1番目の引数はCのspgChooseIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgChooseOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgChooseIn { Datum datum; /* original datum to be indexed */ Datum leafDatum; /* current datum to be stored at leaf */ int level; /* current level (counting from zero) */ Datum datum; /* インデックス付けされる元のデータ */ Datum leafDatum; /* リーフに保存されている現在のデータ */ int level; /* (0から数えた)現在のレベル */ /* Data from current inner tuple */ bool allTheSame; /* tuple is marked all-the-same? */ bool hasPrefix; /* tuple has a prefix? */ Datum prefixDatum; /* if so, the prefix value */ int nNodes; /* number of nodes in the inner tuple */ Datum *nodeLabels; /* node label values (NULL if none) */ /* 現在の内部タプルからのデータ */ bool allTheSame; /* タプルはall-the-sameの印を付けられているか */ bool hasPrefix; /* タプルは接頭辞を持っているか */ Datum prefixDatum; /* そうであれば、接頭辞の値 */ int nNodes; /* 内部タプル内のノード数 */ Datum *nodeLabels; /* ノードのラベルの値(なければNULL) */ } spgChooseIn; typedef enum spgChooseResultType { spgMatchNode = 1, /* descend into existing node */ spgAddNode, /* add a node to the inner tuple */ spgSplitTuple /* split inner tuple (change its prefix) */ spgMatchNode = 1, /* 既存のノードに下がる */ spgAddNode, /* ノードに内部タプルを追加する */ spgSplitTuple /* 内部タプルを分割する(その接頭辞を変更する) */ } spgChooseResultType; typedef struct spgChooseOut { spgChooseResultType resultType; /* action code, see above */ spgChooseResultType resultType; /* アクションコード、上記参照 */ union { struct /* results for spgMatchNode */ struct /* spgMatchNodeの結果 */ { int nodeN; /* descend to this node (index from 0) */ int levelAdd; /* increment level by this much */ Datum restDatum; /* new leaf datum */ int nodeN; /* このノードに下がる(0からのインデックス) */ int levelAdd; /* この分だけレベルを増やす */ Datum restDatum; /* 新しいリーフデータ */ } matchNode; struct /* results for spgAddNode */ struct /* spgAddNodeの結果 */ { Datum nodeLabel; /* new node's label */ int nodeN; /* where to insert it (index from 0) */ Datum nodeLabel; /* 新しいノードのラベル */ int nodeN; /* 挿入する場所(0からのインデックス) */ } addNode; struct /* results for spgSplitTuple */ struct /* spgSplitTupleの結果 */ { /* Info to form new upper-level inner tuple with one child tuple */ bool prefixHasPrefix; /* tuple should have a prefix? */ Datum prefixPrefixDatum; /* if so, its value */ int prefixNNodes; /* number of nodes */ Datum *prefixNodeLabels; /* their labels (or NULL for * no labels) */ int childNodeN; /* which node gets child tuple */ /* 子タプルを1つ持つ新しい上位のレベルの内部タプルを生成するための情報 */ bool prefixHasPrefix; /* タプルは接頭辞を持つか */ Datum prefixPrefixDatum; /* そうならば、その値 */ int prefixNNodes; /* ノード数 */ Datum *prefixNodeLabels; /* そのラベル(ラベルがなければNULL) */ int childNodeN; /* どのタプルが子タプルを得るか */ /* Info to form new lower-level inner tuple with all old nodes */ bool postfixHasPrefix; /* tuple should have a prefix? */ Datum postfixPrefixDatum; /* if so, its value */ /* 古いノードをすべて持つ新しい低位の内部タプルを生成するための情報 */ bool postfixHasPrefix; /* タプルは接頭辞を持つか */ Datum postfixPrefixDatum; /* そうならば、その値 */ } splitTuple; } result; } spgChooseOut;
<structfield>datum</structfield> is the original datum of
<structname>spgConfigIn</structname>.<structfield>attType</structfield>
type that was to be inserted into the index.
<structfield>leafDatum</structfield> is a value of
<structname>spgConfigOut</structname>.<structfield>leafType</structfield>
type, which is initially a result of method
<function>compress</function> applied to <structfield>datum</structfield>
when method <function>compress</function> is provided, or the same value as
<structfield>datum</structfield> otherwise.
<structfield>leafDatum</structfield> can change at lower levels of the tree
if the <function>choose</function> or <function>picksplit</function>
methods change it. When the insertion search reaches a leaf page,
the current value of <structfield>leafDatum</structfield> is what will be stored
in the newly created leaf tuple.
<structfield>level</structfield> is the current inner tuple's level, starting at
zero for the root level.
<structfield>allTheSame</structfield> is true if the current inner tuple is
marked as containing multiple equivalent nodes
(see <xref linkend="spgist-all-the-same"/>).
<structfield>hasPrefix</structfield> is true if the current inner tuple contains
a prefix; if so,
<structfield>prefixDatum</structfield> is its value.
<structfield>nNodes</structfield> is the number of child nodes contained in the
inner tuple, and
<structfield>nodeLabels</structfield> is an array of their label values, or
NULL if there are no labels.
datum
はインデックスに挿入できたspgConfigIn
.attType
型の元データです。
leafDatum
はspgConfigOut
.leafType
型の値です。これは最初は、メソッドcompress
が提供されているならdatum
に適用されたメソッドcompress
の結果で、さもなくばdatum
と同じ値です。
leafDatum
は、choose
やpicksplit
メソッドがこれを変更すると、ツリーのより低いレベルで変化することがあります。
挿入の探索がリーフページに到達するとき、leafDatum
の現在値は、新しく作成されるリーフタプルに格納される値です。
level
は、ルートレベルを0として、現在の内部タプルのレベルを示します。
現在の内部タプルが複数の同等なノードを含むとして印を付けられているとき、allTheSame
をtrueにします(64.3.4.3参照)。
現在の内部タプルが接頭辞を含むとき、hasPrefix
をtrueにします。
このとき、prefixDatum
がその値になります。
nNodes
は内部タプルが含む子ノードの数で、nodeLabels
はそれらのラベル値の配列、あるいはラベルがなければNULLになります。
The <function>choose</function> function can determine either that
the new value matches one of the existing child nodes, or that a new
child node must be added, or that the new value is inconsistent with
the tuple prefix and so the inner tuple must be split to create a
less restrictive prefix.
choose
関数は、新しい値が既存の子ノードの1つとマッチするか、新しい子ノードを追加する必要があるか、あるいは新しい値がタプルの接頭辞と適合しないので内部タプルを分割してより制限のない接頭辞を作成する必要があるか、を決定することができます。
If the new value matches one of the existing child nodes,
set <structfield>resultType</structfield> to <literal>spgMatchNode</literal>.
Set <structfield>nodeN</structfield> to the index (from zero) of that node in
the node array.
Set <structfield>levelAdd</structfield> to the increment in
<structfield>level</structfield> caused by descending through that node,
or leave it as zero if the operator class does not use levels.
Set <structfield>restDatum</structfield> to equal <structfield>leafDatum</structfield>
if the operator class does not modify datums from one level to the
next, or otherwise set it to the modified value to be used as
<structfield>leafDatum</structfield> at the next level.
新しい値が既存の子ノードの1つにマッチしたときは、resultType
をspgMatchNode
にセットします。
nodeN
はノードの配列中のそのノードの(0からの)番号にセットします。
levelAdd
は、そのノードをたどって下がるときに生じたlevel
の増分にセットします。あるいは演算子クラスがレベルを使っていなければ0のままにします。
restDatum
は、演算子クラスがデータをあるレベルから次のレベルに変更しないのであれば、datum
に等しくセットします。そうでなければ、次のレベルでleafDatum
として使われる修正された値にセットします。
If a new child node must be added,
set <structfield>resultType</structfield> to <literal>spgAddNode</literal>.
Set <structfield>nodeLabel</structfield> to the label to be used for the new
node, and set <structfield>nodeN</structfield> to the index (from zero) at which
to insert the node in the node array.
After the node has been added, the <function>choose</function>
function will be called again with the modified inner tuple;
that call should result in an <literal>spgMatchNode</literal> result.
新しい子ノードを追加しなければならないときは、resultType
をspgAddNode
にセットします。
nodeLabel
は、新しいノードで使われるラベルにセットし、nodeN
はノードの配列中の挿入される場所のノードの(0からの)番号にセットします。
ノードを追加した後で、choose
関数を修正された内部タプルを使って再び呼び出しますが、このときは、spgMatchNode
という結果になるはずです。
If the new value is inconsistent with the tuple prefix,
set <structfield>resultType</structfield> to <literal>spgSplitTuple</literal>.
This action moves all the existing nodes into a new lower-level
inner tuple, and replaces the existing inner tuple with a tuple
having a single downlink pointing to the new lower-level inner tuple.
Set <structfield>prefixHasPrefix</structfield> to indicate whether the new
upper tuple should have a prefix, and if so set
<structfield>prefixPrefixDatum</structfield> to the prefix value. This new
prefix value must be sufficiently less restrictive than the original
to accept the new value to be indexed.
Set <structfield>prefixNNodes</structfield> to the number of nodes needed in the
new tuple, and set <structfield>prefixNodeLabels</structfield> to a palloc'd array
holding their labels, or to NULL if node labels are not required.
Note that the total size of the new upper tuple must be no more
than the total size of the tuple it is replacing; this constrains
the lengths of the new prefix and new labels.
Set <structfield>childNodeN</structfield> to the index (from zero) of the node
that will downlink to the new lower-level inner tuple.
Set <structfield>postfixHasPrefix</structfield> to indicate whether the new
lower-level inner tuple should have a prefix, and if so set
<structfield>postfixPrefixDatum</structfield> to the prefix value. The
combination of these two prefixes and the downlink node's label
(if any) must have the same meaning as the original prefix, because
there is no opportunity to alter the node labels that are moved to
the new lower-level tuple, nor to change any child index entries.
After the node has been split, the <function>choose</function>
function will be called again with the replacement inner tuple.
That call may return an <literal>spgAddNode</literal> result, if no suitable
node was created by the <literal>spgSplitTuple</literal> action. Eventually
<function>choose</function> must return <literal>spgMatchNode</literal> to
allow the insertion to descend to the next level.
新しい値がタプルの接頭辞と適合しないときは、resultType
をspgSplitTuple
にセットします。
このアクションは、すべての既存のノードを新しい低位の内部タプルに移動し、新しい低位の内部タプルを指す単一の下向きのリンクを持つ新しいタプルで既存のタプルを置換します。
prefixHasPrefix
は新しい上位のタプルが接頭辞を持つかどうかを示し、持つ場合にはprefixPrefixDatum
をその接頭辞の値にセットします。
インデックスに追加される新しい値を受け入れるため、新しい接頭辞の値は元のものよりも十分に制限の緩いものになっていなければなりません。
prefixNNodes
は新しいタプルで必要なノード数にセットし、prefixNodeLabels
はラベルを保持するためにpallocされた配列に、ノードのラベルが必要でないときはNULLにセットします。
新しい上位のタプルの全サイズは置き換えるタプルの全サイズよりも大きくはないことに注意してください。これは新しい接頭辞と新しいラベルの長さを制約します。
childNodeN
は、新しい低位の内部タプルへ下向きにリンクするノードの(0からの)番号にセットします。
postfixHasPrefix
は、新しい低位のタプルが接頭辞を持つかどうかを示し、持つときにはpostfixPrefixDatum
を接頭辞の値にセットします。
新しい低位に移動したタプルのノードのラベルを変更する機会も、子のインデックスのエントリを変更する機会もありませんから、これら2つの接頭辞と(もしあれば)下向きのリンクのノードのラベルの組み合わせは、元の接頭辞と同じ意味を持つ必要があります。
ノードが分割された後で、置換した内部タプルを使ってchoose
関数を再び呼び出します。
この呼び出しは、spgSplitTuple
アクションにより適切なノードが作られなければ、spgAddNode
という結果になります。
そのうち、choose
がspgMatchNode
を返し、次のレベルに下がる挿入が可能となります。
picksplit
Decides how to create a new inner tuple over a set of leaf tuples. リーフタプルの集合に対し、新しい内部タプルをどうやって作るかを決定します。
The <acronym>SQL</acronym> declaration of the function must look like this: この関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ...
The first argument is a pointer to a <structname>spgPickSplitIn</structname>
C struct, containing input data for the function.
The second argument is a pointer to a <structname>spgPickSplitOut</structname>
C struct, which the function must fill with result data.
1番目の引数はCのspgPickSplitIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgPickSplitOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgPickSplitIn { int nTuples; /* number of leaf tuples */ Datum *datums; /* their datums (array of length nTuples) */ int level; /* current level (counting from zero) */ int nTuples; /* リーフタプルの数 */ Datum *datums; /* そのデータ(長さnTuplesの配列) */ int level; /* (0から数えた)現在のレベル */ } spgPickSplitIn; typedef struct spgPickSplitOut { bool hasPrefix; /* new inner tuple should have a prefix? */ Datum prefixDatum; /* if so, its value */ bool hasPrefix; /* 新しい内部タプルは接頭辞を持つか */ Datum prefixDatum; /* もしそうなら、その値 */ int nNodes; /* number of nodes for new inner tuple */ Datum *nodeLabels; /* their labels (or NULL for no labels) */ int nNodes; /* 新しい内部タプルのノード数 */ Datum *nodeLabels; /* そのラベル(ラベルがなければNULL) */ int *mapTuplesToNodes; /* node index for each leaf tuple */ Datum *leafTupleDatums; /* datum to store in each new leaf tuple */ int *mapTuplesToNodes; /* 各リーフタプルへのノードのインデックス */ Datum *leafTupleDatums; /* 新しい各リーフタプルに保存されているデータ */ } spgPickSplitOut;
<structfield>nTuples</structfield> is the number of leaf tuples provided.
<structfield>datums</structfield> is an array of their datum values of
<structname>spgConfigOut</structname>.<structfield>leafType</structfield>
type.
<structfield>level</structfield> is the current level that all the leaf tuples
share, which will become the level of the new inner tuple.
nTuples
は与えられるリーフタプルの個数です。
datums
はspgConfigOut
.leafType
型のそれらのデータ値の配列です。
level
はすべてのリーフタプルが共有する現在のレベルで、これが新しい内部タプルのレベルになります。
Set <structfield>hasPrefix</structfield> to indicate whether the new inner
tuple should have a prefix, and if so set
<structfield>prefixDatum</structfield> to the prefix value.
Set <structfield>nNodes</structfield> to indicate the number of nodes that
the new inner tuple will contain, and
set <structfield>nodeLabels</structfield> to an array of their label values,
or to NULL if node labels are not required.
Set <structfield>mapTuplesToNodes</structfield> to an array that gives the index
(from zero) of the node that each leaf tuple should be assigned to.
Set <structfield>leafTupleDatums</structfield> to an array of the values to
be stored in the new leaf tuples (these will be the same as the
input <structfield>datums</structfield> if the operator class does not modify
datums from one level to the next).
Note that the <function>picksplit</function> function is
responsible for palloc'ing the
<structfield>nodeLabels</structfield>, <structfield>mapTuplesToNodes</structfield> and
<structfield>leafTupleDatums</structfield> arrays.
hasPrefix
は新しい内部タプルが接頭辞を持つかどうかを示し、持つ場合はprefixDatum
を接頭辞の値にセットします。
nNodes
は新しい内部タプルが含むノードの数を示し、nodeLabels
はそのラベル値の配列に、ノードのラベルが必要でないときはNULLにセットします。
mapTuplesToNodes
は、それぞれのリーフタプルが割り当てられるノードの(0からの)番号の配列にセットします。
leafTupleDatums
は新しいリーフタプルに格納される値の配列にセットします(演算子クラスがデータをあるレベルから次のレベルに変更しなければこれらは入力のdatums
と同じになります)。
picksplit
関数は、nodeLabels
、mapTuplesToNodes
、leafTupleDatums
の配列についてpallocしなければならないことに注意してください。
If more than one leaf tuple is supplied, it is expected that the
<function>picksplit</function> function will classify them into more than
one node; otherwise it is not possible to split the leaf tuples
across multiple pages, which is the ultimate purpose of this
operation. Therefore, if the <function>picksplit</function> function
ends up placing all the leaf tuples in the same node, the core
SP-GiST code will override that decision and generate an inner
tuple in which the leaf tuples are assigned at random to several
identically-labeled nodes. Such a tuple is marked
<literal>allTheSame</literal> to signify that this has happened. The
<function>choose</function> and <function>inner_consistent</function> functions
must take suitable care with such inner tuples.
See <xref linkend="spgist-all-the-same"/> for more information.
2つ以上のリーフタプルを与えた場合、picksplit
関数はそれらを2つ以上のノードに分類すると予想されます。そうでなければ、リーフタプルを複数のページにまたがって分割するという、この操作の究極の目的を実現できないからです。
従って、picksplit
がすべてのリーフタプルを同じノードに置くことになった場合には、SP-GiSTのコアのコードがその決定を覆して内部タプルを生成し、その中の複数の同一のラベルが付けられたノードに、リーフタプルが無作為に割り当てられます。
そのようなタプルは、このことが発生したことを明示するため、allTheSame
と印がつけられます。
choose
関数とinner_consistent
関数は、これらの内部タプルについて、適切な注意をして取り扱わなければなりません。
詳細な情報は64.3.4.3を参照してください。
<function>picksplit</function> can be applied to a single leaf tuple only
in the case that the <function>config</function> function set
<structfield>longValuesOK</structfield> to true and a larger-than-a-page input
value has been supplied. In this case the point of the operation is
to strip off a prefix and produce a new, shorter leaf datum value.
The call will be repeated until a leaf datum short enough to fit on
a page has been produced. See <xref linkend="spgist-limits"/> for
more information.
config
関数がlongValuesOK
をtrueにセットし、1ページよりも大きな入力値を与える場合にのみ、picksplit
を1つだけのリーフタプルに適用できます。
この場合の操作の重要な点は、接頭辞をはがして、新しい、より短いリーフデータの値を生成することです。
この呼出は、1ページに収まる短さのリーフデータが生成されるまで繰り返されます。
詳細な情報は64.3.4.1を参照してください。
inner_consistent
Returns set of nodes (branches) to follow during tree search. ツリーの探索でたどるべきノード(枝)の集合を返します。
The <acronym>SQL</acronym> declaration of the function must look like this: この関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...
The first argument is a pointer to a <structname>spgInnerConsistentIn</structname>
C struct, containing input data for the function.
The second argument is a pointer to a <structname>spgInnerConsistentOut</structname>
C struct, which the function must fill with result data.
1番目の引数はCのspgInnerConsistentIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgInnerConsistentOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgInnerConsistentIn { ScanKey scankeys; /* array of operators and comparison values */ ScanKey orderbys; /* array of ordering operators and comparison * values */ int nkeys; /* length of scankeys array */ int norderbys; /* length of orderbys array */ ScanKey scankeys; /* 演算子と比較する値の配列 */ ScanKey orderbys; /* 順序付け演算子と比較する値の配列 */ int nkeys; /* scankeys配列の長さ */ int norderbys; /* orderbys配列の長さ */ Datum reconstructedValue; /* value reconstructed at parent */ void *traversalValue; /* opclass-specific traverse value */ MemoryContext traversalMemoryContext; /* put new traverse values here */ int level; /* current level (counting from zero) */ bool returnData; /* original data must be returned? */ Datum reconstructedValue; /* 親で再構築された値 */ void *traversalValue; /* 演算子クラスに固有の探索値 */ MemoryContext traversalMemoryContext; /* 新しい探索値をここに入れる */ int level; /* (0から数えた)現在のレベル */ bool returnData; /* 元のデータを返さなければならないか */ /* Data from current inner tuple */ bool allTheSame; /* tuple is marked all-the-same? */ bool hasPrefix; /* tuple has a prefix? */ Datum prefixDatum; /* if so, the prefix value */ int nNodes; /* number of nodes in the inner tuple */ Datum *nodeLabels; /* node label values (NULL if none) */ /* 現在の内部タプルからのデータ */ bool allTheSame; /* タプルはall-the-sameと印が付けられているか */ bool hasPrefix; /* タプルは接頭辞を持つか */ Datum prefixDatum; /* もしそうなら、接頭辞の値 */ int nNodes; /* 内部タプルの中のノード数 */ Datum *nodeLabels; /* ノードのラベルの値(なければNULL) */ } spgInnerConsistentIn; typedef struct spgInnerConsistentOut { int nNodes; /* number of child nodes to be visited */ int *nodeNumbers; /* their indexes in the node array */ int *levelAdds; /* increment level by this much for each */ Datum *reconstructedValues; /* associated reconstructed values */ void **traversalValues; /* opclass-specific traverse values */ double **distances; /* associated distances */ int nNodes; /* 訪れるべき子ノードの数 */ int *nodeNumbers; /* ノードの配列でのそのインデックス */ int *levelAdds; /* この分だけそれぞれレベルを挙げる */ Datum *reconstructedValues; /* 関連する再構築された値 */ void **traversalValues; /* 演算子クラスに固有の探索値 */ double **distances; /* 関連する距離 */ } spgInnerConsistentOut;
The array <structfield>scankeys</structfield>, of length <structfield>nkeys</structfield>,
describes the index search condition(s). These conditions are
combined with AND — only index entries that satisfy all of
them are interesting. (Note that <structfield>nkeys</structfield> = 0 implies
that all index entries satisfy the query.) Usually the consistent
function only cares about the <structfield>sk_strategy</structfield> and
<structfield>sk_argument</structfield> fields of each array entry, which
respectively give the indexable operator and comparison value.
In particular it is not necessary to check <structfield>sk_flags</structfield> to
see if the comparison value is NULL, because the SP-GiST core code
will filter out such conditions.
The array <structfield>orderbys</structfield>, of length <structfield>norderbys</structfield>,
describes ordering operators (if any) in the same manner.
<structfield>reconstructedValue</structfield> is the value reconstructed for the
parent tuple; it is <literal>(Datum) 0</literal> at the root level or if the
<function>inner_consistent</function> function did not provide a value at the
parent level.
<structfield>traversalValue</structfield> is a pointer to any traverse data
passed down from the previous call of <function>inner_consistent</function>
on the parent index tuple, or NULL at the root level.
<structfield>traversalMemoryContext</structfield> is the memory context in which
to store output traverse values (see below).
<structfield>level</structfield> is the current inner tuple's level, starting at
zero for the root level.
<structfield>returnData</structfield> is <literal>true</literal> if reconstructed data is
required for this query; this will only be so if the
<function>config</function> function asserted <structfield>canReturnData</structfield>.
<structfield>allTheSame</structfield> is true if the current inner tuple is
marked <quote>all-the-same</quote>; in this case all the nodes have the
same label (if any) and so either all or none of them match the query
(see <xref linkend="spgist-all-the-same"/>).
<structfield>hasPrefix</structfield> is true if the current inner tuple contains
a prefix; if so,
<structfield>prefixDatum</structfield> is its value.
<structfield>nNodes</structfield> is the number of child nodes contained in the
inner tuple, and
<structfield>nodeLabels</structfield> is an array of their label values, or
NULL if the nodes do not have labels.
配列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
は真になります。この場合、(もしあるなら)全てのノードが同じラベルを持ち、問い合わせに全てが一致するか、全く一致しないかのいずれかになります(64.3.4.3を参照)。
現在の内部タプルがプレフィックスを含んでいるならhasPrefix
は真になります。その場合、prefixDatum
がその値です。
nNodes
は内部タプルに含まれる子ノードの数で、nodeLabels
はそれらのラベル値の配列、あるいは、ノードがラベルを持たないならNULLです。
<structfield>nNodes</structfield> must be set to the number of child nodes that
need to be visited by the search, and
<structfield>nodeNumbers</structfield> must be set to an array of their indexes.
If the operator class keeps track of levels, set
<structfield>levelAdds</structfield> to an array of the level increments
required when descending to each node to be visited. (Often these
increments will be the same for all the nodes, but that's not
necessarily so, so an array is used.)
If value reconstruction is needed, set
<structfield>reconstructedValues</structfield> to an array of the values
reconstructed for each child node to be visited; otherwise, leave
<structfield>reconstructedValues</structfield> as NULL.
The reconstructed values are assumed to be of type
<structname>spgConfigOut</structname>.<structfield>leafType</structfield>.
(However, since the core system will do nothing with them except
possibly copy them, it is sufficient for them to have the
same <literal>typlen</literal> and <literal>typbyval</literal>
properties as <structfield>leafType</structfield>.)
If ordered search is performed, set <structfield>distances</structfield>
to an array of distance values according to <structfield>orderbys</structfield>
array (nodes with lowest distances will be processed first). Leave it
NULL otherwise.
If it is desired to pass down additional out-of-band information
(<quote>traverse values</quote>) to lower levels of the tree search,
set <structfield>traversalValues</structfield> to an array of the appropriate
traverse values, one for each child node to be visited; otherwise,
leave <structfield>traversalValues</structfield> as NULL.
Note that the <function>inner_consistent</function> function is
responsible for palloc'ing the
<structfield>nodeNumbers</structfield>, <structfield>levelAdds</structfield>,
<structfield>distances</structfield>,
<structfield>reconstructedValues</structfield>, and
<structfield>traversalValues</structfield> arrays in the current memory context.
However, any output traverse values pointed to by
the <structfield>traversalValues</structfield> array should be allocated
in <structfield>traversalMemoryContext</structfield>.
Each traverse value must be a single palloc'd chunk.
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
Returns true if a leaf tuple satisfies a query. リーフタプルが問い合わせを満たす場合、trueを返します。
The <acronym>SQL</acronym> declaration of the function must look like this: この関数のSQL宣言は以下のようになります。
CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...
The first argument is a pointer to a <structname>spgLeafConsistentIn</structname>
C struct, containing input data for the function.
The second argument is a pointer to a <structname>spgLeafConsistentOut</structname>
C struct, which the function must fill with result data.
1番目の引数はCのspgLeafConsistentIn
構造体へのポインタで、関数の入力データを含みます。
2番目の引数はCのspgLeafConsistentOut
構造体へのポインタで、関数が結果のデータを入れます。
typedef struct spgLeafConsistentIn { ScanKey scankeys; /* array of operators and comparison values */ ScanKey orderbys; /* array of ordering operators and comparison * values */ int nkeys; /* length of scankeys array */ int norderbys; /* length of orderbys array */ ScanKey scankeys; /* 演算子と比較する値の配列 */ ScanKey orderbys; /* 順序付け演算子と比較する値の配列 */ int nkeys; /* scankeys配列の長さ */ int norderbys; /* orderbys配列の長さ */ Datum reconstructedValue; /* value reconstructed at parent */ void *traversalValue; /* opclass-specific traverse value */ int level; /* current level (counting from zero) */ bool returnData; /* original data must be returned? */ Datum reconstructedValue; /* 親で再構築された値 */ void *traversalValue; /* 演算子クラスに固有の探索値 */ int level; /* (0から数えた)現在のレベル */ bool returnData; /* 元のデータを返さなければならないか */ Datum leafDatum; /* datum in leaf tuple */ Datum leafDatum; /* リーフタプルのデータ */ } spgLeafConsistentIn; typedef struct spgLeafConsistentOut { Datum leafValue; /* reconstructed original data, if any */ bool recheck; /* set true if operator must be rechecked */ bool recheckDistances; /* set true if distances must be rechecked */ double *distances; /* associated distances */ Datum leafValue; /* もしあれば、再構築された元のデータ */ bool recheck; /* 演算子を再チェックする必要があればtrue */ bool recheckDistances; /* 距離を再チェックする必要があればtrue */ double *distances; /* 関連する距離 */ } spgLeafConsistentOut;
The array <structfield>scankeys</structfield>, of length <structfield>nkeys</structfield>,
describes the index search condition(s). These conditions are
combined with AND — only index entries that satisfy all of
them satisfy the query. (Note that <structfield>nkeys</structfield> = 0 implies
that all index entries satisfy the query.) Usually the consistent
function only cares about the <structfield>sk_strategy</structfield> and
<structfield>sk_argument</structfield> fields of each array entry, which
respectively give the indexable operator and comparison value.
In particular it is not necessary to check <structfield>sk_flags</structfield> to
see if the comparison value is NULL, because the SP-GiST core code
will filter out such conditions.
The array <structfield>orderbys</structfield>, of length <structfield>norderbys</structfield>,
describes the ordering operators in the same manner.
<structfield>reconstructedValue</structfield> is the value reconstructed for the
parent tuple; it is <literal>(Datum) 0</literal> at the root level or if the
<function>inner_consistent</function> function did not provide a value at the
parent level.
<structfield>traversalValue</structfield> is a pointer to any traverse data
passed down from the previous call of <function>inner_consistent</function>
on the parent index tuple, or NULL at the root level.
<structfield>level</structfield> is the current leaf tuple's level, starting at
zero for the root level.
<structfield>returnData</structfield> is <literal>true</literal> if reconstructed data is
required for this query; this will only be so if the
<function>config</function> function asserted <structfield>canReturnData</structfield>.
<structfield>leafDatum</structfield> is the key value of
<structname>spgConfigOut</structname>.<structfield>leafType</structfield>
stored in the current leaf tuple.
配列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
のキーの値です。
The function must return <literal>true</literal> if the leaf tuple matches the
query, or <literal>false</literal> if not. In the <literal>true</literal> case,
if <structfield>returnData</structfield> is <literal>true</literal> then
<structfield>leafValue</structfield> must be set to the value (of type
<structname>spgConfigIn</structname>.<structfield>attType</structfield>)
originally supplied to be indexed for this leaf tuple. Also,
<structfield>recheck</structfield> may be set to <literal>true</literal> if the match
is uncertain and so the operator(s) must be re-applied to the actual
heap tuple to verify the match.
If ordered search is performed, set <structfield>distances</structfield>
to an array of distance values according to <structfield>orderbys</structfield>
array. Leave it NULL otherwise. If at least one of returned distances
is not exact, set <structfield>recheckDistances</structfield> to true.
In this case, the executor will calculate the exact distances after
fetching the tuple from the heap, and will reorder the tuples if needed.
この関数は、リーフタプルが問い合わせにマッチすればtrue
を返し、マッチしなければfalse
を返します。
true
の場合、returnData
がtrue
であれば、leafValue
を、このリーフタプルにインデックス付けするために元々提供された(spgConfigIn
.attType
型の)値に設定しなければなりません。
また、マッチするかどうかが不確実で、マッチするかの確認のために実際のヒープタプルに演算子を再適用しなければならないときは、recheck
がtrue
にセットされることがあります。
順序付け検索を実行するなら、orderbys
配列に従ってdistances
に距離の値の配列を設定します。
そうでなければNULLのままにします。
返される距離の少なくとも1つが正確でないのなら、recheckDistances
にtrueを設定します。
この場合、エグゼキュータはヒープからタプルを取得した後正確な距離を計算し、必要ならタプルを並べ替えます。
The optional user-defined methods are: オプションのユーザ定義メソッドは以下です。
Datum compress(Datum in)
Converts a data item into a format suitable for physical storage in
a leaf tuple of the index. It accepts a value of type
<structname>spgConfigIn</structname>.<structfield>attType</structfield>
and returns a value of type
<structname>spgConfigOut</structname>.<structfield>leafType</structfield>.
The output value must not contain an out-of-line TOAST pointer.
インデックスのリーフタプルでデータ項目を物理ストレージに適した形式に変換します。
spgConfigIn
.attType
型の値を受け付け、spgConfigOut
.leafType
型の値を返します。
出力値は行に収まらないTOASTを含んでいてはいけません。
Note: the <function>compress</function> method is only applied to
values to be stored. The consistent methods receive query
<structfield>scankeys</structfield> unchanged, without transformation
using <function>compress</function>.
注意: compress
メソッドは格納される値にのみ適用されます。
適合するメソッドはcompress
を使って変換することなく、問い合わせのscankeys
をそのまま受け取ります。
options
Defines a set of user-visible parameters that control operator class behavior. 演算子クラスの振舞いを制御するユーザに可視のパラメータの集合を定義します。
The <acronym>SQL</acronym> declaration of the function must look like this: この関数のSQL宣言は以下のようになります。
CREATE OR REPLACE FUNCTION my_options(internal) RETURNS void AS 'MODULE_PATHNAME' LANGUAGE C STRICT;
The function is passed a pointer to a <structname>local_relopts</structname>
struct, which needs to be filled with a set of operator class
specific options. The options can be accessed from other support
functions using the <literal>PG_HAS_OPCLASS_OPTIONS()</literal> and
<literal>PG_GET_OPCLASS_OPTIONS()</literal> macros.
関数にはlocal_relopts
構造体へのポインタが渡されますが、構造体を演算子クラスに固有のオプションの集合で満たすことが必要です。
オプションはマクロPG_HAS_OPCLASS_OPTIONS()
とPG_GET_OPCLASS_OPTIONS()
を使って他のサポート関数からアクセスできます。
Since the representation of the key in <acronym>SP-GiST</acronym> is flexible, it may depend on user-specified parameters. SP-GiSTでのキーの表現には柔軟性がありますので、ユーザに固有のパラメータに依存するかもしれません。
All the SP-GiST support methods are normally called in a short-lived
memory context; that is, <varname>CurrentMemoryContext</varname> will be reset
after processing of each tuple. It is therefore not very important to
worry about pfree'ing everything you palloc. (The <function>config</function>
method is an exception: it should try to avoid leaking memory. But
usually the <function>config</function> method need do nothing but assign
constants into the passed parameter struct.)
SP-GiSTのすべてのサポートメソッドは、通常は短期間有効なメモリコンテキスト内で呼び出されます。つまり、それぞれのタプルについて処理した後でCurrentMemoryContext
はリセットされます。
したがって、pallocしたものすべてについてpfreeすることを気にかけることはあまり重要ではありません。
(config
メソッドは例外で、メモリリークを避けるようにする必要があります。
しかし、通常はconfig
メソッドは、パラメータとして渡された構造体に定数を代入する以外、何もする必要がありません。)
If the indexed column is of a collatable data type, the index collation
will be passed to all the support methods, using the standard
<function>PG_GET_COLLATION()</function> mechanism.
インデックス付けされた列が照合可能なデータ型の場合、インデックスの照合は、標準的なPG_GET_COLLATION()
の仕組みを使ってすべてのサポートメソッドに渡されます。
This section covers implementation details and other tricks that are useful for implementers of <acronym>SP-GiST</acronym> operator classes to know. この節では、SP-GiSTの演算子クラスを実装する人にとって知っていると役に立つ、実装についての詳細とその他の秘訣について説明します。
Individual leaf tuples and inner tuples must fit on a single index page
(8kB by default). Therefore, when indexing values of variable-length
data types, long values can only be supported by methods such as radix
trees, in which each level of the tree includes a prefix that is short
enough to fit on a page, and the final leaf level includes a suffix also
short enough to fit on a page. The operator class should set
<structfield>longValuesOK</structfield> to true only if it is prepared to arrange for
this to happen. Otherwise, the <acronym>SP-GiST</acronym> core will
reject any request to index a value that is too large to fit
on an index page.
それぞれのリーフタプルおよび内部タプルは1つのインデックスページ内(デフォルトで8kB)に収まらなければなりません。
従って、可変長のデータ型の値をインデックス付けするときは、長い値は基数木のようなメソッドによってのみサポートされます。つまり、ツリーのそれぞれのレベルではページに収まる短さの接頭辞を含み、最後のリーフレベルでは、やはりページに収まる短さの接尾辞を含む、というようなものです。
このようなことが発生する場合の対応の準備ができている場合のみ、演算子クラスはlongValuesOK
を真にセットするべきです。
そうでなければ、SP-GiSTのコアは、インデックスページに収めるには大きすぎる値についてのインデックス付け要求を拒絶します。
Likewise, it is the operator class's responsibility that inner tuples do not grow too large to fit on an index page; this limits the number of child nodes that can be used in one inner tuple, as well as the maximum size of a prefix value. 同様に、内部タプルが大きくなりすぎてインデックスページに収まらない、ということにならないようにするのは、演算子クラスの責任です。 これにより、1つの内部タプルで使うことができる子ノードの数、および接頭辞の値の最大サイズが制限されます。
Another limitation is that when an inner tuple's node points to a set
of leaf tuples, those tuples must all be in the same index page.
(This is a design decision to reduce seeking and save space in the
links that chain such tuples together.) If the set of leaf tuples
grows too large for a page, a split is performed and an intermediate
inner tuple is inserted. For this to fix the problem, the new inner
tuple <emphasis>must</emphasis> divide the set of leaf values into more than one
node group. If the operator class's <function>picksplit</function> function
fails to do that, the <acronym>SP-GiST</acronym> core resorts to
extraordinary measures described in <xref linkend="spgist-all-the-same"/>.
内部タプルのノードがリーフタプルの集合を指しているとき、それらのタプルはすべて同じインデックスページ内になければならない、という制限もあります。
(これは、シークの回数を減らし、そのようなタプルを一つにつなげるリンクに必要なスペースを減らす、という設計上の決定によるものです。)
リーフタプルの集合が大きくなって1ページに収まらなくなると、分割が実行され、中間の内部タプルが挿入されます。
これで問題を解決するためには、新しい内部タプルは、リーフの値の集合を2つ以上のノードのグループに分割しなければなりません。
演算子クラスのpicksplit
関数がそれをするのに失敗したときは、SP-GiSTのコアは、64.3.4.3に記述されている特別な手段に頼ることになります。
When <structfield>longValuesOK</structfield> is true, it is expected
that successive levels of the <acronym>SP-GiST</acronym> tree will
absorb more and more information into the prefixes and node labels of
the inner tuples, making the required leaf datum smaller and smaller,
so that eventually it will fit on a page.
To prevent bugs in operator classes from causing infinite insertion
loops, the <acronym>SP-GiST</acronym> core will raise an error if the
leaf datum does not become any smaller within ten cycles
of <function>choose</function> method calls.
longValuesOK
が真であれば、SP-GiSTツリーの後続のレベルは、より多くの情報を接頭辞と内部タプルのノードラベルへと吸収し、要求されるリーフデータより小さくして、最終的には1ページに収まるようになることが期待されます。
演算子クラスのバグが無限の挿入ループを引き起こすのを防ぐために、choose
メソッドの呼び出しの10サイクル以内でリーフデータが少しも小さくならなければ、SP-GiSTコアはエラーを発生します。
Some tree algorithms use a fixed set of nodes for each inner tuple;
for example, in a quad-tree there are always exactly four nodes
corresponding to the four quadrants around the inner tuple's centroid
point. In such a case the code typically works with the nodes by
number, and there is no need for explicit node labels. To suppress
node labels (and thereby save some space), the <function>picksplit</function>
function can return NULL for the <structfield>nodeLabels</structfield> array,
and likewise the <function>choose</function> function can return NULL for
the <structfield>prefixNodeLabels</structfield> array during
a <literal>spgSplitTuple</literal> action.
This will in turn result in <structfield>nodeLabels</structfield> being NULL during
subsequent calls to <function>choose</function> and <function>inner_consistent</function>.
In principle, node labels could be used for some inner tuples and omitted
for others in the same index.
木構造のアルゴリズムには、それぞれの内部タプルに対して固定された集合のノードを使うものがあります。
例えば四分木では、内部タプルの中心点の周りの4つの象限に対応するちょうど4つのノードが必ずあります。
このような場合、コードは典型的には数字を使ったノードで動作し、明示的なノードラベルは必要ありません。
ノードラベルを使わない(そしてそれによりいくらかのスペースを節約する)ために、picksplit
関数はnodeLabels
配列としてNULLを返すことができ、同様にchoose
関数はspgSplitTuple
アクションの間prefixNodeLabels
配列としてNULLを返すことができます。
この結果、その後のchoose
関数およびinner_consistent
関数の呼び出しにおいてもnodeLabels
はNULLになります。
原則として、ノードラベルは同じインデックス中の一部の内部タプルに使い、他の内部タプルには省略する、ということができます。
When working with an inner tuple having unlabeled nodes, it is an error
for <function>choose</function> to return <literal>spgAddNode</literal>, since the set
of nodes is supposed to be fixed in such cases.
ラベルのないノードを持つ内部タプルを処理するときに、choose
がspgAddNode
を返すのはエラーです。というのは、この場合、ノードの集合は固定されていると想定されるからです。
The <acronym>SP-GiST</acronym> core can override the results of the
operator class's <function>picksplit</function> function when
<function>picksplit</function> fails to divide the supplied leaf values into
at least two node categories. When this happens, the new inner tuple
is created with multiple nodes that each have the same label (if any)
that <function>picksplit</function> gave to the one node it did use, and the
leaf values are divided at random among these equivalent nodes.
The <literal>allTheSame</literal> flag is set on the inner tuple to warn the
<function>choose</function> and <function>inner_consistent</function> functions that the
tuple does not have the node set that they might otherwise expect.
picksplit
が入力のリーフ値を少なくとも2つのノード分類に分割できなかった場合、SP-GiSTのコアは演算子クラスのpicksplit
関数の結果を無効にすることがあります。
これが起きると、複数のノードを持つ新しい内部タプルが作成されます。それぞれのノードは、picksplit
が一つのノードに付与したもの(あれば)と同じラベルを持ち、リーフ値はこれらの等価なノード間でランダムに分割されます。
内部タプルにはallTheSame
のフラグがセットされ、choose
関数およびinner_consistent
関数に対し、そのタプルが通常期待されるようなノードの集合を持っていないことを警告します。
When dealing with an <literal>allTheSame</literal> tuple, a <function>choose</function>
result of <literal>spgMatchNode</literal> is interpreted to mean that the new
value can be assigned to any of the equivalent nodes; the core code will
ignore the supplied <structfield>nodeN</structfield> value and descend into one
of the nodes at random (so as to keep the tree balanced). It is an
error for <function>choose</function> to return <literal>spgAddNode</literal>, since
that would make the nodes not all equivalent; the
<literal>spgSplitTuple</literal> action must be used if the value to be inserted
doesn't match the existing nodes.
allTheSame
の処理において、choose
のspgMatchNode
という結果は、新しい値は等価なノードのどれに割り当てられても良い、という意味に解釈されます。
コアのコードは入力されたnodeN
の値を無視し、(ツリーの平衡を保つために)ノードの1つにランダムに降りていきます。
choose
がspgAddNode
を返すのはエラーです。というのは、そうするとすべてのノードが等価ではなくなるからです。
挿入する値が既存のノードとマッチしない時は、spgSplitTuple
のアクションを使わなければなりません。
When dealing with an <literal>allTheSame</literal> tuple, the
<function>inner_consistent</function> function should return either all or none
of the nodes as targets for continuing the index search, since they are
all equivalent. This may or may not require any special-case code,
depending on how much the <function>inner_consistent</function> function normally
assumes about the meaning of the nodes.
allTheSame
のタプルの処理において、すべてのノードは等価なので、inner_consistent
関数は、インデックス検索を続けるためのターゲットとして、すべてのノードを返すか、ノードを1つも返さないかのいずれかであるべきです。
このために、特殊ケースを扱うコードが必要になるかもしれませんし、必要ないかもしれません。それは、inner_consistent
関数が、通常、ノードの意味についてどの程度のことを仮定しているかに依存します。
The <productname>PostgreSQL</productname> source distribution includes
several examples of index operator classes for <acronym>SP-GiST</acronym>,
as described in <xref linkend="spgist-builtin-opclasses-table"/>. Look
into <filename>src/backend/access/spgist/</filename>
and <filename>src/backend/utils/adt/</filename> to see the code.
PostgreSQLのソースコードの配布物には、表 64.2に示すように、SP-GiSTのインデックス演算子クラスの例がいくつか含まれています。
コードを見るにはsrc/backend/access/spgist/
とsrc/backend/utils/adt/
を調べてみてください。