PostgreSQL 可擴展性

2021-09-15 11:43 更新

SP-GiST提供了一個高抽象層次的接口,要求訪問方法開發(fā)者實現(xiàn)與一個給定數(shù)據(jù)類型相關的幾種方法。SP-GiST核心負責高效的磁盤映射和搜索樹結(jié)構(gòu)。它也會處理并發(fā)和日志。

SP-GiST樹的葉子元組包含與被索引列數(shù)據(jù)類型相同的值。在根層的葉子元組總是包含原始的被索引數(shù)據(jù)值,但是在較下層的葉子元組可能只含有一個壓縮后的表示,例如一個后綴。在這種情況下,操作符類支持函數(shù)必須能夠使用從內(nèi)部元組計算出來的信息重構(gòu)出原始的值,這些內(nèi)部元組指的是在到達葉子層的過程中穿過的元組。

內(nèi)部元組更加復雜,因為它們是搜索樹的分支點。每一個內(nèi)部元組包含一個或者更多個結(jié)點,結(jié)點表示一個具有相似葉子值的組。一個結(jié)點包含一個向下的鏈接,這個鏈接可以導向另一個較下層的內(nèi)部元組,或者是由位于同一索引頁面的葉子元組組成的一個短列表。每一個結(jié)點通常還有一個標簽來描述它,例如,在一個 radix 樹中結(jié)點標簽可以是串值的下一個字符(或者,如果一種操作符類對于所有內(nèi)部元組使用一個固定的節(jié)點集合,則它可以省略節(jié)點標簽,見 第 65.4.2 節(jié))。可選地,一個內(nèi)部元組可以有一個前綴值來描述它所有的成員。在一個 radix 樹中前綴可以是所表示的串的公共前綴。前綴值并不一定非要是一個真正的前綴,它可以是操作符類需要的任何數(shù)據(jù)。例如,在一個四叉樹中它可以存儲用于劃分四個象限的中心點。一個四叉樹的內(nèi)部元組則可以包含對應于圍繞該中心點的象限的四個結(jié)點。

某些樹算法要求當前元組所在層(或深度)的知識,因此SP-GiST核心為操作符類提供了機會以便在沿著樹下降時管理層計數(shù)。當需要重組被表示的值時,這也可以為增量地重構(gòu)過程提供支持,這還可以為沿著樹下降時向下層傳遞附加數(shù)據(jù)(稱為貫穿值)提供支持。

注意

SP-GiST核心代碼會關注空項。盡管SP-GiST索引確實可以存儲被索引列中的空值,但這對索引操作符類代碼是隱藏的:不會有空索引項或搜索條件會被傳遞給操作符類方法(我們假定SP-GiST操作符是嚴格的并且因此無法成功處理空值)。因此這里不會進一步討論空值。

一個SP-GiST的索引操作符類必須提供五個用戶定義的方法,并且兩個是可選的。所有五個強制的方法都接受兩個internal參數(shù),其中第一個是一個指針,它指向一個包含用于支持方法的值的 C 結(jié)構(gòu)。而第二個參數(shù)也是一個指針,它指向?qū)⒎胖幂敵鲋档?C 結(jié)構(gòu)。其中四個強制的函數(shù)只返回void,因為它們的所有結(jié)果都出現(xiàn)在輸出結(jié)構(gòu)中。但是 leaf_consistent會返回一個boolean結(jié)果。這些方法不能修改它們的輸入結(jié)構(gòu)的任何域。在所有情況下,調(diào)用用戶定義的方法之前輸出結(jié)構(gòu)都被初始化為零??蛇x的第六個方法compress接受要被索引的datum作為唯一的參數(shù)并且返回適合于在葉子元組中物理存儲的值。可選的第七個方法 options 接受一個指向 C 結(jié)構(gòu)的 internal 指針,其中應放置 opclass 特定的參數(shù),并返回 void。

五個強制的用戶定義的方法是:

config

返回關于索引實現(xiàn)的靜態(tài)信息,包括前綴的數(shù)據(jù)類型的OID以及結(jié)點標簽數(shù)據(jù)類型。

這個函數(shù)的SQL聲明必須看起來像這樣:

CREATE FUNCTION my_config(internal, internal) RETURNS void ...

第一個參數(shù)是一個指向spgConfigIn C 結(jié)構(gòu)的指針,包含該函數(shù)的輸入數(shù)據(jù)。第二個參數(shù)是一個指向spgConfigOut C 結(jié)構(gòu)的指針,函數(shù)必須將結(jié)果數(shù)據(jù)填充在其中。

typedef struct spgConfigIn
{
    Oid         attType;        /* 要被索引的數(shù)據(jù)類型 */
} spgConfigIn;

typedef struct spgConfigOut
{
    Oid         prefixType;     /* 內(nèi)部元組前綴的數(shù)據(jù)類型 */
    Oid         labelType;      /* 內(nèi)部元組結(jié)點標簽的數(shù)據(jù)類型 */
    Oid         leafType;       /* 葉子元組值的數(shù)據(jù)類型 */
    bool        canReturnData;  /* 操作符類能重構(gòu)原始數(shù)據(jù) */
    bool        longValuesOK;   /* 操作符類能處理值 > 1 頁 */
} spgConfigOut;

為了支持多態(tài)的索引操作符類,attType要被傳入;對于普通固定數(shù)據(jù)類型的操作符類,它將總是取相同的值,因此可以被忽略。

對于不使用前綴的操作符類,prefixType可以被設置為VOIDOID。同樣,對于不使用結(jié)點標簽的操作符類,labelType可以被設置為VOIDOID。如果操作符類能夠重構(gòu)出原來提供的被索引值,則canReturnData應該被設置為真。只有當 attType是變長的并且操作符類能夠?qū)㈤L值通過反復的添加后綴分段時,longValuesOK才應當被設置為真(參見第 65.4.1 節(jié))。

leafType通常和attType相同。為了向后兼容性的原因,方法config可以將leafType保留成未初始化的形態(tài),那樣會得到與把leafType設置為等于 attType時一樣的效果。當attTypeleafType不同時,可選的方法compress必須被提供。方法compress負責把要被索引的數(shù)據(jù)從attType轉(zhuǎn)換為 leafType。注意:兩種一致函數(shù)都會得到未更改的scankeys,也不會使用compress轉(zhuǎn)換。

choose

為將一個新值插入到一個內(nèi)部元組選擇一種方法。

該函數(shù)的SQL聲明必須看起來像這樣:

CREATE FUNCTION my_choose(internal, internal) RETURNS void ...

第一個參數(shù)是一個指向spgChooseIn C 結(jié)構(gòu)的指針,包含該函數(shù)的輸入數(shù)據(jù)。第二個參數(shù)是一個指向spgChooseOut C 結(jié)構(gòu)的指針,函數(shù)必須將結(jié)果數(shù)據(jù)填充在其中。

typedef struct spgChooseIn
{
    Datum       datum;          /* 要被索引的原始數(shù)據(jù) */
    Datum       leafDatum;      /* 要被存儲在葉子中的當前數(shù)據(jù) */
    int         level;          /* 當前層(從零計數(shù)) */

    /* 來自當前內(nèi)部元組的數(shù)據(jù) */
    bool        allTheSame;     /* tuple is marked all-the-same? */
    bool        hasPrefix;      /* 元組有一個前綴? */
    Datum       prefixDatum;    /* 如果有,前綴值 */
    int         nNodes;         /* 內(nèi)部元組中的結(jié)點數(shù)目 */
    Datum      *nodeLabels;     /* 結(jié)點標簽值(如果沒有為 NULL) */
} spgChooseIn;

typedef enum spgChooseResultType
{
    spgMatchNode = 1,           /* 下降到現(xiàn)有結(jié)點 */
    spgAddNode,                 /* 向內(nèi)部元組增加一個結(jié)點 */
    spgSplitTuple               /* 劃分內(nèi)部元組(修改它的前綴) */
} spgChooseResultType;

typedef struct spgChooseOut
{
    spgChooseResultType resultType;     /* 動作代碼,見上文 */
    union
    {
        struct                  /* 用于spgMatchNode的結(jié)果 */
        {
            int         nodeN;      /* 下降到這個結(jié)點(索引從 0 開始) */
            int         levelAdd;   /* 這次匹配增加的層 */
            Datum       restDatum;  /* 新葉數(shù)據(jù) */
        }           matchNode;
        struct                  /* 用于spgAddNode的結(jié)果 */
        {
            Datum       nodeLabel;  /* 新結(jié)點的標簽 */
            int         nodeN;      /* 在哪里插入它(索引從 0 開始) */
        }           addNode;
        struct                  /* 用于spgSplitTuple的結(jié)果 */
        {
            /* 來自有一個子元組的新上層內(nèi)部元組的信息 */
            bool        prefixHasPrefix;    /* 元組能有前綴嗎? */
            Datum       prefixPrefixDatum;  /* 如果有,前綴值 */
            int         prefixNNodes;       /* 節(jié)點的數(shù)目 */
            Datum      *prefixNodeLabels;   /* 它們的標簽(NULL表示無標簽)*/
            int         childNodeN;         /* 哪個節(jié)點有子元組 */

            /* 來自放有所有舊結(jié)點的新下層內(nèi)部元組的信息 */
            bool        postfixHasPrefix;   /* 元組能有前綴嗎? */
            Datum       postfixPrefixDatum; /* 如果有,前綴值 */
        }           splitTuple;
    }           result;
} spgChooseOut;

datum是要被插入到該索引中的spgConfigIn.attType類型的原始數(shù)據(jù)。leafDatum是一個spgConfigOut.leafType類型的值,它最初是方法 compress應用到datum上的結(jié)果(如果提供了方法compress)或者是和datum相同的值(如果沒有提供compress方法)。但是如果choosepicksplit改變了它,那么位于樹的較低層的 leafDatum值可能會改變。當插入搜索到達一個葉子頁,leafDatum的當前值就會被存儲在新創(chuàng)建的葉子元組中。level是當前內(nèi)部元組的層次,根層是 0 。如果當前內(nèi)部元組被標記為包含多個等價節(jié)點(見第 65.4.3 節(jié)), allTheSame為真。如果當前內(nèi)部元組有一個前綴,hasPrefix為真,如果這樣,prefixDatum為前綴值。nNodes是包含在內(nèi)部元組中子結(jié)點的數(shù)量,并且nodeLabels是這些子結(jié)點的標簽值的數(shù)組,如果沒有標簽則為 NULL。

choose函數(shù)能決定新值是匹配一個現(xiàn)有子結(jié)點,或是必須增加一個新的子節(jié)點,亦或是新值和元組的前綴不一致并且因此該內(nèi)部元組必須被分裂來創(chuàng)建限制性更低的前綴。

如果新值匹配一個現(xiàn)有的子結(jié)點,將resultType設置為spgMatchNode。將nodeN設置為該結(jié)點在結(jié)點數(shù)組中的索引(從零開始)。將levelAdd設置為傳到該結(jié)點導致的level增加,或者在操作符類不使用層數(shù)時將它置為零。如果操作符類沒有把數(shù)據(jù)從一層修改到下一層,將 restDatum設置為等于datum,否則將它設置為在下一層用作leafDatum的被修改后的值。

如果必須增加一個新的子結(jié)點,將resultType設置為spgAddNode。將nodeLabel設置為在新結(jié)點中使用的標簽,并將nodeN設置為插入該結(jié)點的位置在結(jié)點數(shù)組中的索引(從零開始)。在結(jié)點被增加之后,choose函數(shù)將被再次調(diào)用并使用修改后的內(nèi)部元組,那時將會導致一個 spgMatchNode結(jié)果。

如果新值和元組的前綴不一致,將resultType設置為spgSplitTuple。這個動作將所有現(xiàn)有的結(jié)點移動到一個新的下層內(nèi)部元組,并且將現(xiàn)有的內(nèi)部元組用一個新元組替換,該元組只有一個到那個新的下層內(nèi)部元組的向下鏈接。將prefixHasPrefix設置為指示新的上層元組是否具有一個前綴,并且在如果有前綴時設置 prefixPrefixDatum為前綴值。這個新的前綴值必須比原來的值要足夠?qū)捤梢员隳軌蚪邮軐⒈凰饕男轮?。?code class="structfield">prefixNNodes設置為新元組中所需的節(jié)點數(shù),并且將prefixNodeLabels設置為一個已分配的保存它們的標簽的數(shù)組,或者在不要求節(jié)點標簽時設置為NULL。注意新上層元組的總尺寸必須不超過它所替換的元組的總尺寸,這限制了新前綴和新標簽的長度。將 childNodeN設置為將下鏈到新的下層內(nèi)元組的節(jié)點的索引(從零開始)。設置postfixHasPrefix表示新的下層內(nèi)元組是否應該有一個前綴,并且在應該有前綴的情況下設置postfixPrefixDatum為前綴值。這兩種前綴以及下鏈節(jié)點的標簽(如果有)的組合必須具有與原始前綴相同的含義,因為沒有機會修改被移動到新下層元組的節(jié)點標簽,也不能更改任何子索引項。在該節(jié)點被分裂后,將再次用替換的內(nèi)元組調(diào)用 choose函數(shù)。如果spgSplitTuple動作沒有創(chuàng)建出合適的節(jié)點,該調(diào)用可以返回一個spgAddNode結(jié)果。最終choose必須返回spgMatchNode以允許插入下降到下一層次中。

picksplit

決定如何在一組葉子元組上創(chuàng)建一個新的內(nèi)部元組。

該函數(shù)的SQL聲明必須看起來像這樣:

CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ...

第一個參數(shù)是一個指向spgPickSplitIn C 結(jié)構(gòu)的指針,包含該函數(shù)的輸入數(shù)據(jù)。第二個參數(shù)是一個指向spgPickSplitOut C 結(jié)構(gòu)的指針,函數(shù)必須將結(jié)果數(shù)據(jù)填充在其中。

typedef struct spgPickSplitIn
{
    int         nTuples;        /* 葉子元組的數(shù)量 */
    Datum      *datums;         /* 它們的數(shù)據(jù)(長度為 nTuples 的數(shù)組) */
    int         level;          /* 當前層次(從零開始計) */
} spgPickSplitIn;

typedef struct spgPickSplitOut
{
    bool        hasPrefix;      /* 新內(nèi)部元組應該有一個前綴嗎? */
    Datum       prefixDatum;    /* 如果有,前綴值 */

    int         nNodes;         /* 新內(nèi)部元組的結(jié)點數(shù) */
    Datum      *nodeLabels;     /* 它們的標簽(沒有標簽則為NULL) */

    int        *mapTuplesToNodes;   /* 每一個葉子元組的結(jié)點索引 */
    Datum      *leafTupleDatums;    /* 存儲在每一個新葉子元組中的數(shù)據(jù) */
} spgPickSplitOut;

nTuples是提供的葉子元組的數(shù)目。datums是它們的spgConfigOut.leafType類型的數(shù)據(jù)值的數(shù)組。level是所有這些葉子元組共享的當前層,它將成為新內(nèi)部元組所在的層次。

hasPrefix設置為指示新內(nèi)部元組是否應該有前綴,并且如果有前綴則將prefixDatum設置成前綴值。將nNodes設置為新內(nèi)部元組將包含的結(jié)點數(shù)目,并且將nodeLabels設置為它們的標簽值的數(shù)組或者 NULL(如果結(jié)點不要求標簽)。將 mapTuplesToNodes設置為一個數(shù)組,該數(shù)組告訴每一個葉子元組應該被賦予的結(jié)點的索引(從零開始)。將leafTupleDatums設置為由將要被存儲在新葉子元組中的值構(gòu)成的一個數(shù)組(如果操作符類不將數(shù)據(jù)從一層修改到下一層,這些值將和輸入的datums相同)。注意picksplit函數(shù)負責為 nodeLabels、mapTuplesToNodesleafTupleDatums數(shù)組進行 palloc。

如果提供了多于一個葉子元組,picksplit被寄望于將它們分類到多余一個結(jié)點中;否則不可能將葉子元組劃分到多個頁面,這也是這個操作的終極目的。因此,如果picksplit函數(shù)結(jié)束時把所有葉子元組放在同一個結(jié)點中,核心SP-GiST代碼將覆蓋該決定,并且生成一個內(nèi)部元組,將葉子元組隨機分配到多個不同標簽的結(jié)點。這樣一個元組被標記為allTheSame來表示發(fā)生了這種情況。 chooseinner_consistent函數(shù)必須對這樣的內(nèi)部元組采取合適的處理。詳見第 65.4.3 節(jié)

picksplit只能在一種情況下被應用在單獨一個葉子元組上,這種情況是config函數(shù)將longValuesOK設置為真并且提供了一個長于一頁的輸入。在這種情況中,該操作的要點是剝離一個前綴并且產(chǎn)生一個新的、較短的葉子數(shù)據(jù)值。這種調(diào)用將被重復直到產(chǎn)生一個足夠短能夠放入到一頁的葉子數(shù)據(jù)。詳見第 65.4.1 節(jié)

inner_consistent

在樹搜索過程中返回一組要追求的結(jié)點(分支)。

該函數(shù)的SQL聲明必須看起來像這樣:

CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...

第一個參數(shù)是一個指向spgInnerConsistentIn C 結(jié)構(gòu)的指針,包含該函數(shù)的輸入數(shù)據(jù)。第二個參數(shù)是一個指向spgInnerConsistentOut C 結(jié)構(gòu)的指針,函數(shù)必須將結(jié)果數(shù)據(jù)填充在其中。

typedef struct spgInnerConsistentIn
{
    ScanKey     scankeys;       /* 操作符和比較值的數(shù)組 */
    ScanKey     orderbys;       /* 排序運算符和比較數(shù)組 
                                 * 值 */
    int         nkeys;          /* 掃描鍵數(shù)組的長度 */
    int         norderbys;      /* 排序數(shù)組的長度 */

    Datum       reconstructedValue;     /* 在父結(jié)點中的重構(gòu)值 */
    void       *traversalValue; /* 操作符類相關的貫穿值 */
    MemoryContext traversalMemoryContext;   /* 把新的貫穿值放在這里 */
    int         level;          /* 當前層次(從零開始計) */
    bool        returnData;     /* 是否必須返回原始數(shù)據(jù)? */

    /* 來自當前內(nèi)元組的數(shù)據(jù) */
    bool        allTheSame;     /* 元組被標記為完全相同? */
    bool        hasPrefix;      /* 元組有前綴? */
    Datum       prefixDatum;    /* 如果有,前綴值 */
    int         nNodes;         /* 內(nèi)元組中的結(jié)點數(shù) */
    Datum      *nodeLabels;     /* 結(jié)點標簽值(沒有就是 NULL) */
} spgInnerConsistentIn;

typedef struct spgInnerConsistentOut
{
    int         nNodes;         /* 要被訪問的子結(jié)點數(shù) */
    int        *nodeNumbers;    /* 它們在結(jié)點數(shù)組中的索引 */
    int        *levelAdds;      /* 為每個子結(jié)點要增加的層數(shù) */
    Datum      *reconstructedValues;    /* 相關的重構(gòu)值 */
    void      **traversalValues;        /* 操作符類相關的貫穿值 */
    double    **distances;              /* 關聯(lián)距離 */
} spgInnerConsistentOut;

長度為nkeys的數(shù)組scankeys描述了索引搜索條件。這些條件用 AND 組合 — 只對滿足所有條件的索引項感興趣(注意,nkeys = 0 表示所有索引項滿足該查詢)。通常一致函數(shù)只關心每個數(shù)組項的sk_strategysk_argument,它們分別給出了可索引操作符和比較值。特別要說明的是,沒有必要去檢查sk_flags來看比較值是否為 NULL,因為 SP-GiST 的核心代碼會過濾這樣的條件。 數(shù)組orderbys,長度norderbys,以相同的方式描述排序運算符(如果有)。 reconstructedValue是用于父元組的重構(gòu)值,在根層時或者如果 inner_consistent函數(shù)沒有在父層提供一個值時,它為(Datum) 0。reconstructedValue總是spgConfigOut.leafType類型。traversalValue是任意貫穿數(shù)據(jù)的指針,該數(shù)據(jù)由父索引元組上的上一次 inner_consistent調(diào)用傳遞下來,在根層上這個指針為 NULL。traversalMemoryContext是用于存放輸出的貫穿值(見下文)的內(nèi)存上下文。level是當前內(nèi)元組層次,根層是 0。如果這個查詢要求重構(gòu)的數(shù)據(jù),returnDatatrue。如果 config斷言canReturnDatareturnData只會是true。如果當前的內(nèi)元組被標記為完全一樣,那么allTheSame為真。在這種情況下,所有的結(jié)點都具有相同的標簽(如果有),而且它們要么全部匹配該查詢,要么一個都不匹配查詢(見 第 65.4.3 節(jié))。如果當前內(nèi)元組包含一個前綴,則hasPrefix為真。如果這樣,prefixDatum就是該前綴的值。nNodes是包含在內(nèi)元組中的子結(jié)點的數(shù)量,nodeLabels是它們的標簽值的數(shù)組。當然如果結(jié)點沒有標簽,這個數(shù)組就為 NULL。

nNodes必須被設置為搜索需要訪問的子結(jié)點數(shù),并且nodeNumbers必須被設置為子結(jié)點索引的數(shù)組。如果操作符類跟蹤層次,把levelAdds設置成一個數(shù)組,其中說明了在下降到要被訪問的每一個結(jié)點時需要增加的層數(shù)(通常這些增量對于所有結(jié)點都是相同的,但是并不一定如此,所以需要使用一個數(shù)組)。如果需要值重構(gòu),將 reconstructedValues設置為一個spgConfigOut.leafType類型值的數(shù)組,這些值是為要被訪問的每一個子節(jié)點構(gòu)造的。否則,把reconstructedValues留為NULL。 如果執(zhí)行有序搜索,根據(jù)orderbys數(shù)組,設置 distances為距離值數(shù)組。(距離最短的節(jié)點將首先處理)。 否則,保留它為空。 如果想要把額外的帶外信息(貫穿值)向下傳遞給樹搜索的較低層,可以把traversalValues設置成合適的貫穿值的數(shù)組,其中每一個元素用于一個要被訪問的子節(jié)點。如果不需要傳遞額外的帶外信息,則把traversalValues設置為 NULL。注意,inner_consistent函數(shù)負責在當前內(nèi)存上下文中分配nodeNumbers、levelAdds、distances、reconstructedValuestraversalValues數(shù)組。不過,任何由traversalValues數(shù)組指向的輸出貫穿值應該在traversalMemoryContext中分配。每一個貫穿值必須是一個單獨分配的塊(chunk)。

leaf_consistent

如果一個葉子元組滿足一個查詢則返回真。

該函數(shù)的SQL聲明必須看起來像這樣:

CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...

第一個參數(shù)是一個指向spgLeafConsistentIn C 結(jié)構(gòu)的指針,包含該函數(shù)的輸入數(shù)據(jù)。第二個參數(shù)是一個指向spgLeafConsistentOut C 結(jié)構(gòu)的指針,函數(shù)必須將結(jié)果數(shù)據(jù)填充在其中。

typedef struct spgLeafConsistentIn
{
    ScanKey     scankeys;       /* 操作符和比較值的數(shù)組 */
    ScanKey     orderbys;       /* 排序運算符和比較數(shù)組 
                                 * 值 */
    int         nkeys;          /* 掃描鍵數(shù)組的長度 */
    int         norderbys;      /* 排序數(shù)組的長度 */

    Datum       reconstructedValue;     /* 在父節(jié)點重構(gòu)的值 */
    void       *traversalValue; /* 操作符類相關的貫穿值 */
    int         level;          /* 當前層次(從零開始計) */
    bool        returnData;     /* 是否必須返回原始數(shù)據(jù)? */

    Datum       leafDatum;      /* 葉子元組中的數(shù)據(jù) */
} spgLeafConsistentIn;

typedef struct spgLeafConsistentOut
{
    Datum       leafValue;        /* 重構(gòu)的原始數(shù)據(jù),如果有 */
    bool        recheck;          /* 如果操作符必須被重新檢查則設為真 */
    bool        recheckDistances; /* 如果距離必須被重新檢查,設置為 true */
    double     *distances;        /* 關聯(lián)距離 */
} spgLeafConsistentOut;

長度為nkeys的數(shù)組scankeys描述了索引搜索條件。這些條件用 AND 組合在一起 — 只有滿足所有條件的索引項才滿足該查詢(注意nkeys = 0 表示所有的索引項都滿足查詢)。通常 consistent 函數(shù)值關注每一個數(shù)組項的sk_strategysk_argument域,它們分別給出了可索引操作符和比較值。特別是它無需檢查sk_flags來檢查比較值是否為 NULL,因為 SP-GiST 核心代碼將過濾掉這類條件。 數(shù)組orderbys,長度norderbys,以相同的方式描述排序運算符(如果有)。 reconstructedValue是為父元組重構(gòu)的值,在根層或者當 inner_consistent沒有提供父層上的值時,它是(Datum) 0。reconstructedValue總是spgConfigOut.leafType類型。traversalValue是任意貫穿數(shù)據(jù)的指針,該數(shù)據(jù)由父索引元組上的上一次 inner_consistent調(diào)用傳遞下來,在根層上這個指針為 NULL。level是當前的葉子元組所在的層次,根層為零。如果這個查詢要求重構(gòu)的數(shù)據(jù),則returnDatatrue。只有在config函數(shù)主張了canReturnData時才會如此。 leafDatum是存儲在當前葉子元組中的spgConfigOut.leafType的鍵值。

如果葉子元組匹配查詢,則該函數(shù)必須返回true,否則返回false。在返回true的情況中,如果returnDatatrue,則leafValue必須被設置為最初為構(gòu)建這個葉子元組提供的 spgConfigIn.attType類型值。還有,如果匹配是不確定的并且操作符必須被重新應用在實際堆元組上驗證匹配,則recheck會被設置為true。 如果執(zhí)行有序搜索,則根據(jù)orderbys數(shù)組設置distances為距離值數(shù)組。否則,保留它為空。 如果至少有一個返回的距離不準確,則recheckDistances為 true。 在這種情況下,執(zhí)行器將從堆中獲取元組之后計算精確的距離,并根據(jù)需要重新排序元組。

可選的用戶定義的方法是:

Datum compress(Datum in)

將數(shù)據(jù)項轉(zhuǎn)換成一種適合索引頁面的葉子元組中物理存儲方式的格式。它接受spgConfigIn.attType值并且返回spgConfigOut.leafType值。輸出值不應該被TOAST。

options

定義一組控制運算符類行為的用戶可見參數(shù)。

函數(shù)的 SQL 聲明必須如下所示:

CREATE OR REPLACE FUNCTION my_options(internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

該函數(shù)被傳遞一個指向 local_relopts 結(jié)構(gòu)的指針,該結(jié)構(gòu)需要填充一組特定于運算符類的選項。 可以使用 PG_HAS_OPCLASS_OPTIONS()PG_GET_OPCLASS_OPTIONS() 宏從其他支持函數(shù)訪問這些選項。

由于 SP-GiST 中鍵的表示是靈活的,它可能取決于用戶指定的參數(shù)。

所有的 SP-GiST 支持方法通常都在一個短期存在的內(nèi)存上下文中被調(diào)用,即在處理完每一個元組后CurrentMemoryContext將被重置。因此并不需要操心 pfree 你 palloc 的任何東西(config方法是一個特例:它應該避免泄漏內(nèi)存。但是通常config方法只需要為傳出的參數(shù)結(jié)構(gòu)賦常數(shù)值)。

如果被索引的列是一種可排序的數(shù)據(jù)類型,索引的排序規(guī)則將被使用標準的PG_GET_COLLATION()機制傳遞給所有的支持方法。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號