バージョンごとのドキュメント一覧

64.3. SP-GiSTインデックス #

<title>SP-GiST Indexes</title>

64.3.1. はじめに #

<title>Introduction</title>

<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 <!&#45;- URL will be changed &#45;-> <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サイトにあります。

64.3.2. 組み込み演算子クラス #

<title>Built-in Operator Classes</title>

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演算子クラス

<title>Built-in <acronym>SP-GiST</acronym> Operator Classes</title>
名前インデックス可能な演算子順序付け演算子
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>&lt;-&gt;</literal> ordering operator, which enables the k-nearest neighbor (<literal>k-NN</literal>) search over indexed point or polygon data sets. quad_point_opskd_point_opspoly_ops演算子クラスは<->順序付け演算子をサポートしますので、インデックス付けされた点や多角形のデータ集合に対してk近傍(k-NN)探索が可能です。

64.3.3. 拡張性 #

<title>Extensibility</title>

<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_consistentbooleanの結果を返します。 メソッドは、その入力構造体のどのフィールドも変更してはいけません。 どんな場合でも、出力構造体はユーザ定義メソッドを呼び出す前にゼロに初期化されます。 オプションの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 &gt; 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"/>). 接頭辞を使わない演算子クラスでは、prefixTypeVOIDOIDに設定することができます。 同様に、ノードラベルを使わない演算子クラスでは、labelTypeVOIDOIDに設定することができます。 演算子クラスが、元々提供されていたインデックスの値を再構築できるときは、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>. attTypeleafTypeが異なる場合には、オプションのメソッド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型の元データです。 leafDatumspgConfigOut.leafType型の値です。これは最初は、メソッドcompressが提供されているならdatumに適用されたメソッドcompressの結果で、さもなくばdatumと同じ値です。 leafDatumは、choosepicksplitメソッドがこれを変更すると、ツリーのより低いレベルで変化することがあります。 挿入の探索がリーフページに到達するとき、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つにマッチしたときは、resultTypespgMatchNodeにセットします。 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. 新しい子ノードを追加しなければならないときは、resultTypespgAddNodeにセットします。 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. 新しい値がタプルの接頭辞と適合しないときは、resultTypespgSplitTupleにセットします。 このアクションは、すべての既存のノードを新しい低位の内部タプルに移動し、新しい低位の内部タプルを指す単一の下向きのリンクを持つ新しいタプルで既存のタプルを置換します。 prefixHasPrefixは新しい上位のタプルが接頭辞を持つかどうかを示し、持つ場合にはprefixPrefixDatumをその接頭辞の値にセットします。 インデックスに追加される新しい値を受け入れるため、新しい接頭辞の値は元のものよりも十分に制限の緩いものになっていなければなりません。 prefixNNodesは新しいタプルで必要なノード数にセットし、prefixNodeLabelsはラベルを保持するためにpallocされた配列に、ノードのラベルが必要でないときはNULLにセットします。 新しい上位のタプルの全サイズは置き換えるタプルの全サイズよりも大きくはないことに注意してください。これは新しい接頭辞と新しいラベルの長さを制約します。 childNodeNは、新しい低位の内部タプルへ下向きにリンクするノードの(0からの)番号にセットします。 postfixHasPrefixは、新しい低位のタプルが接頭辞を持つかどうかを示し、持つときにはpostfixPrefixDatumを接頭辞の値にセットします。 新しい低位に移動したタプルのノードのラベルを変更する機会も、子のインデックスのエントリを変更する機会もありませんから、これら2つの接頭辞と(もしあれば)下向きのリンクのノードのラベルの組み合わせは、元の接頭辞と同じ意味を持つ必要があります。 ノードが分割された後で、置換した内部タプルを使ってchoose関数を再び呼び出します。 この呼び出しは、spgSplitTupleアクションにより適切なノードが作られなければ、spgAddNodeという結果になります。 そのうち、choosespgMatchNodeを返し、次のレベルに下がる挿入が可能となります。

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は与えられるリーフタプルの個数です。 datumsspgConfigOut.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関数は、nodeLabelsmapTuplesToNodesleafTupleDatumsの配列について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 &mdash; 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と同じtyplentypbyval属性を持っていれば十分です。) 順序付け検索を実行するなら、orderbys配列に従ってdistancesに距離の値の配列を設定します(距離の最も近いノードが最初に処理されます)。 そうでなければNULLのままにします。 追加の外部情報(探索値)をツリー探索の下位レベルに渡したい場合は、traversalValuesを適切な探索値、訪れるそれぞれの子ノードについて1つの配列にセットします。 それ以外の場合はtraversalValuesをNULLのままにします。 inner_consistent関数は、現在のメモリコンテキスト内のnodeNumberslevelAddsdistancesreconstructedValuestraversalValuesの配列について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 &mdash; 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の場合、returnDatatrueであれば、leafValueを、このリーフタプルにインデックス付けするために元々提供された(spgConfigIn.attType型の)値に設定しなければなりません。 また、マッチするかどうかが不確実で、マッチするかの確認のために実際のヒープタプルに演算子を再適用しなければならないときは、rechecktrueにセットされることがあります。 順序付け検索を実行するなら、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()の仕組みを使ってすべてのサポートメソッドに渡されます。

64.3.4. 実装 #

<title>Implementation</title>

This section covers implementation details and other tricks that are useful for implementers of <acronym>SP-GiST</acronym> operator classes to know. この節では、SP-GiSTの演算子クラスを実装する人にとって知っていると役に立つ、実装についての詳細とその他の秘訣について説明します。

64.3.4.1. SP-GiSTの制限 #

<title>SP-GiST Limits</title>

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コアはエラーを発生します。

64.3.4.2. ノードラベルのないSP-GiST #

<title>SP-GiST Without Node Labels</title>

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. ラベルのないノードを持つ内部タプルを処理するときに、choosespgAddNodeを返すのはエラーです。というのは、この場合、ノードの集合は固定されていると想定されるからです。

64.3.4.3. All-the-Same内部タプル #

<title><quote>All-the-Same</quote> Inner Tuples</title>

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の処理において、choosespgMatchNodeという結果は、新しい値は等価なノードのどれに割り当てられても良い、という意味に解釈されます。 コアのコードは入力されたnodeNの値を無視し、(ツリーの平衡を保つために)ノードの1つにランダムに降りていきます。 choosespgAddNodeを返すのはエラーです。というのは、そうするとすべてのノードが等価ではなくなるからです。 挿入する値が既存のノードとマッチしない時は、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関数が、通常、ノードの意味についてどの程度のことを仮定しているかに依存します。

64.3.5. 例 #

<title>Examples</title>

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/を調べてみてください。