PostgreSQL 可擴(kuò)展性

2021-09-15 11:43 更新

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

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

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

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

注意

SP-GiST核心代碼會(huì)關(guān)注空項(xiàng)。盡管SP-GiST索引確實(shí)可以存儲(chǔ)被索引列中的空值,但這對(duì)索引操作符類(lèi)代碼是隱藏的:不會(huì)有空索引項(xiàng)或搜索條件會(huì)被傳遞給操作符類(lèi)方法(我們假定SP-GiST操作符是嚴(yán)格的并且因此無(wú)法成功處理空值)。因此這里不會(huì)進(jìn)一步討論空值。

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

五個(gè)強(qiáng)制的用戶定義的方法是:

config

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

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

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

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

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

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

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

對(duì)于不使用前綴的操作符類(lèi),prefixType可以被設(shè)置為VOIDOID。同樣,對(duì)于不使用結(jié)點(diǎn)標(biāo)簽的操作符類(lèi),labelType可以被設(shè)置為VOIDOID。如果操作符類(lèi)能夠重構(gòu)出原來(lái)提供的被索引值,則canReturnData應(yīng)該被設(shè)置為真。只有當(dāng) attType是變長(zhǎng)的并且操作符類(lèi)能夠?qū)㈤L(zhǎng)值通過(guò)反復(fù)的添加后綴分段時(shí),longValuesOK才應(yīng)當(dāng)被設(shè)置為真(參見(jiàn)第 65.4.1 節(jié))。

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

choose

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

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

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

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

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

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

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

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

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

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

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

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

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

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

picksplit

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

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

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

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

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

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

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

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

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

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

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

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

inner_consistent

在樹(shù)搜索過(guò)程中返回一組要追求的結(jié)點(diǎn)(分支)。

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

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

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

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

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

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

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

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

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

leaf_consistent

如果一個(gè)葉子元組滿足一個(gè)查詢(xún)則返回真。

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

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

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

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

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

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

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

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

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

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

Datum compress(Datum in)

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

options

定義一組控制運(yùn)算符類(lèi)行為的用戶可見(jiàn)參數(shù)。

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

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

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

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

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

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


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)