W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在傳統(tǒng)上,實現(xiàn)一種新的索引訪問方法意味著很多困難的工作。開發(fā)者必須要理解數(shù)據(jù)庫的內(nèi)部工作,例如鎖管理器和預(yù)寫式日志。GiST接口有一個高層的抽象,要求訪問方法實現(xiàn)者只實現(xiàn)要被訪問的數(shù)據(jù)類型的語義。GiST層本身會處理并發(fā)、日志和對樹結(jié)構(gòu)的搜索。
這種可擴展性不應(yīng)該與其他標準搜索樹對于它們所處理的數(shù)據(jù)上的可擴展性混淆。例如,PostgreSQL支持可擴展的 B 樹和哈希索引。也就是說你可以用PostgreSQL在任何你想要的數(shù)據(jù)類型上構(gòu)建一個 B 樹或哈希。但是 B 樹只支持范圍謂詞(<
、=
、
>
),而哈希索引支持等值查詢。
這樣如果你用一個PostgreSQL的 B 樹索引一個圖像集合,你只能發(fā)出例如“imagex 等于 imagey 嗎”、“imagex 小于 imagey 嗎”以及“imagex 大于 imagey 嗎”的查詢。取決于你如何在這種上下文中定義 “等于”、“小于”和“大于”,這可能會有用。但是,通過使用一個基于GiST的索引,你可以創(chuàng)建提問領(lǐng)域相關(guān)問題的方法,可能是“找所有馬的圖片”或者 “找所有曝光過度的圖片”。
建立一個GiST訪問方法并讓其運行的所有工作是實現(xiàn)幾個用戶定義的方法,它們定義了樹中鍵的行為。當然這些方法必須相當特別來支持特別的查詢,但是對于所有標準查詢(B 樹、R 樹等)它們相對直接。簡而言之,GiST在可擴展性之上結(jié)合了通用型、代碼重用和一個干凈的接口。
一個用于GiST的索引操作符類必須提供五種方法,并且還有五種可選的方法。 索引的正確性由正確實現(xiàn)的same
、consistent
和union
方法保證,而索引的效率(尺寸和速度)將依賴于penalty
和
picksplit
方法。 兩種可選的方法是compress
和decompress
,它們允許一個索引能對內(nèi)部數(shù)據(jù)使用一種不同于被其索引的數(shù)據(jù)的類型。 葉子是被索引的數(shù)據(jù)類型,而其他樹結(jié)點可以是任何 C 結(jié)構(gòu)(但是你仍必須遵循PostgreSQL的數(shù)據(jù)類型規(guī)則,見用于可變尺寸數(shù)據(jù)的varlena
)。
如果樹的內(nèi)部數(shù)據(jù)類型在 SQL 層上存在,可以使用CREATE OPERATOR CLASS
命令的STORAGE
選項。 可選的第八個方法是distance
,如果操作符類希望支持有序掃描(最近鄰搜索)就需要它。 如果該操作符希望支持只用索引的掃描,則需要可選的第九個方法fetch
,但
compress
方法被省略時不需要。 如果操作符類提供了用戶指定的參數(shù),則需要第10個options
方法選項。
consistent
給定一個索引項p
和一個查詢值q
,這個函數(shù)決定該索引項是否與該查詢“一致”,就是說:是否該索引項表示的行使得謂詞“indexed_column
indexable_operator
q
”為真?對于一個葉子索引項,這等效于測試索引條件;而對于一個內(nèi)部樹結(jié)點,這會決定是否需要掃描由該樹結(jié)點表示的索引子樹。當結(jié)果為
true
時,還必須返回一個recheck
標志。這指示該謂詞一定為真或者只是可能為真。如果recheck
= false
那么該索引已經(jīng)完全測試過該謂詞條件,而如果recheck
= true
則該行只是一個候選匹配。在那種情況下,系統(tǒng)將根據(jù)實際的行值自動評估
indexable_operator
來看它是否真的是一個匹配。這允許GiST同時支持有損和無損的索引結(jié)構(gòu)。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_consistent(internal, data_type, smallint, oid, internal)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
在 C 模塊中匹配的代碼則應(yīng)該遵循這樣的框架:
PG_FUNCTION_INFO_V1(my_consistent);
Datum
my_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
data_type *query = PG_GETARG_DATA_TYPE_P(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
data_type *key = DatumGetDataType(entry->key);
bool retval;
/*
* 根據(jù)策略、鍵和查詢確定返回值。
*
* 使用 GIST_LEAF(entry) 可以了解當前函數(shù)是在索引樹的哪里被調(diào)用,
* 這在支持例如 = 操作符時很方便(可以在非葉子節(jié)點中檢查非空 union()
* 以及在葉子節(jié)點中檢查等值)。
*/
*recheck = true; /* 如果檢查是準確的則返回 false */
PG_RETURN_BOOL(retval);
}
這里,key
是該索引中的一個元素而query
是在該索引中查找的值。StrategyNumber
參數(shù)指示在你的操作符類中哪個操作符被應(yīng)用 — 它匹配CREATE OPERATOR CLASS
命令中的操作符編號之一。
取決于在操作符類中包含著哪些操作符,query
的數(shù)據(jù)類型可能隨著操作符而變化,因為它可能是該操作符右手邊的任何類型,而這種類型可能和出現(xiàn)在其左手邊的被索引數(shù)據(jù)類型不同(上面的代碼框架假定只有一種類型;如果不是這樣,取query
參數(shù)值的方式可能必須取決于操作符)。我們推薦讓consistent
函數(shù)的 SQL 聲明對
query
參數(shù)使用操作符類的被索引數(shù)據(jù)類型,即便實際類型可能是其他依賴于操作符的類型也是如此。
union
這個方法聯(lián)合樹中的信息。給定一組項,這個函數(shù)產(chǎn)生一個新的索引項,它表示所有給定的項。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_union(internal, internal)
RETURNS storage_type
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
在 C 模塊中匹配的代碼則應(yīng)該遵循這樣的框架:
PG_FUNCTION_INFO_V1(my_union);
Datum
my_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GISTENTRY *ent = entryvec->vector;
data_type *out,
*tmp,
*old;
int numranges,
i = 0;
numranges = entryvec->n;
tmp = DatumGetDataType(ent[0].key);
out = tmp;
if (numranges == 1)
{
out = data_type_deep_copy(tmp);
PG_RETURN_DATA_TYPE_P(out);
}
for (i = 1; i < numranges; i++)
{
old = out;
tmp = DatumGetDataType(ent[i].key);
out = my_union_implementation(out, tmp);
}
PG_RETURN_DATA_TYPE_P(out);
}
如你所見,在這個框架中我們處理一種數(shù)據(jù)類型union(X, Y, Z) = union(union(X, Y), Z)
。通過在這個GiST支持方法中實現(xiàn)正確的聯(lián)合算法,支持不是這種情況的數(shù)據(jù)類型足夠簡單。
union
函數(shù)的結(jié)果必須是該索引的存儲類型的一個值,它可能與被索引列的類型不同,也可能相同。union
函數(shù)應(yīng)該返回一個指針指向新palloc()
的內(nèi)存。不能照原樣返回輸入值,即使沒有類型改變也不能。
如上所示,union
函數(shù)的第一個internal
參數(shù)實際上是一個GistEntryVector
指針。第二個參數(shù)是一個指向整數(shù)變量的指針,它可以被忽略(過去要求union
函數(shù)將其結(jié)果值的尺寸存儲在這個變量中,但現(xiàn)在這已不再必要)。
compress
把數(shù)據(jù)項轉(zhuǎn)換成適合于一個索引頁面中物理存儲的格式。如果compress
方法被省略,數(shù)據(jù)項會被不加修改地存儲在索引中。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_compress(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
在 C 模塊中匹配的代碼則應(yīng)該遵循這樣的框架:
PG_FUNCTION_INFO_V1(my_compress);
Datum
my_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
if (entry->leafkey)
{
/* 用一個壓縮版本替換 entry->key */
compressed_data_type *compressed_data = palloc(sizeof(compressed_data_type));
/* 從 entry->key ... 填充 *compressed_data */
retval = palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(compressed_data),
entry->rel, entry->page, entry->offset, FALSE);
}
else
{
/* 通常我們不需要對非葉子項做任何事情 */
retval = entry;
}
PG_RETURN_POINTER(retval);
}
當然,為了壓縮你的葉結(jié)點,你必須把compressed_data_type
改編成你正在轉(zhuǎn)換到的指定類型。
decompress
將一個數(shù)據(jù)項的存儲表達轉(zhuǎn)換成該操作符類中其他GiST方法能夠操縱的格式。如果decompress
方法被省略,則假設(shè)其他GiST方法能夠直接在存儲的數(shù)據(jù)格式上工作(decompress
不一定要是compress
方法的逆操作,特別是如果compress
是有損的,那么
decompress
是不可能準確地重構(gòu)原始數(shù)據(jù)的。decompress
也不一定與fetch
等效,因為其他GiST方法可能不需要數(shù)據(jù)的完整重構(gòu))。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT;
在 C 模塊中匹配的代碼則應(yīng)該遵循這樣的框架:
PG_FUNCTION_INFO_V1(my_decompress); Datum my_decompress(PG_FUNCTION_ARGS) { PG_RETURN_POINTER(PG_GETARG_POINTER(0)); }
上述框架適合于不需要解壓的情況(但是,將該方法一并省去當然更加容易,并且在這種情況下推薦這樣做)。
penalty
返回一個值,它指示在樹的一個特定分支插入新項的“代價”。項將被插入到樹中具有最小penalty
的路徑中。penalty
返回的值應(yīng)該為非負。如果一個賦值被返回,它將被當作零來處理。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_penalty(internal, internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT; -- in some cases penalty functions need not be strict
在 C 模塊中匹配的代碼則應(yīng)該遵循這樣的框架:
PG_FUNCTION_INFO_V1(my_penalty);
Datum
my_penalty(PG_FUNCTION_ARGS)
{
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
data_type *orig = DatumGetDataType(origentry->key);
data_type *new = DatumGetDataType(newentry->key);
*penalty = my_penalty_implementation(orig, new);
PG_RETURN_POINTER(penalty);
}
由于歷史原因,penalty
函數(shù)不只是返回一個float
結(jié)果,而是必須把該值存儲在由第三個參數(shù)指定的位置。雖然傳回該參數(shù)的地址符合慣例,但返回值本身可以被忽略。
penalty
函數(shù)對于索引的好性能是至關(guān)重要的。在插入時,當要選擇在樹中的哪個位置加入新項時,這個函數(shù)有助于決定應(yīng)該順著哪個分支進行。在查詢時,索引越平衡,查找越快速。
picksplit
當需要一次索引頁面分裂時,這個函數(shù)決定在該頁面上哪些項會留在舊頁面上,以及哪些項會移動到新頁面上。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_picksplit(internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
在 C 模塊中匹配的代碼則應(yīng)該遵循這樣的框架:
PG_FUNCTION_INFO_V1(my_picksplit);
Datum
my_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
OffsetNumber maxoff = entryvec->n - 1;
GISTENTRY *ent = entryvec->vector;
int i,
nbytes;
OffsetNumber *left,
*right;
data_type *tmp_union;
data_type *unionL;
data_type *unionR;
GISTENTRY **raw_entryvec;
maxoff = entryvec->n - 1;
nbytes = (maxoff + 1) * sizeof(OffsetNumber);
v->spl_left = (OffsetNumber *) palloc(nbytes);
left = v->spl_left;
v->spl_nleft = 0;
v->spl_right = (OffsetNumber *) palloc(nbytes);
right = v->spl_right;
v->spl_nright = 0;
unionL = NULL;
unionR = NULL;
/* 初始化裸的項向量。 */
raw_entryvec = (GISTENTRY **) malloc(entryvec->n * sizeof(void *));
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
raw_entryvec[i] = &(entryvec->vector[i]);
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
{
int real_index = raw_entryvec[i] - entryvec->vector;
tmp_union = DatumGetDataType(entryvec->vector[real_index].key);
Assert(tmp_union != NULL);
/*
* 選擇在哪里放置索引項并且相應(yīng)地更新 unionL 和 unionR。
* 把項追加到 v->spl_left 或者 v->spl_right,并且設(shè)置好計數(shù)器。
*/
if (my_choice_is_left(unionL, curl, unionR, curr))
{
if (unionL == NULL)
unionL = tmp_union;
else
unionL = my_union_implementation(unionL, tmp_union);
*left = real_index;
++left;
++(v->spl_nleft);
}
else
{
/*
* 和在右邊的過程相同
*/
}
}
v->spl_ldatum = DataTypeGetDatum(unionL);
v->spl_rdatum = DataTypeGetDatum(unionR);
PG_RETURN_POINTER(v);
}
注意picksplit
函數(shù)的結(jié)果通過修改傳入的v
結(jié)構(gòu)來傳遞。盡管傳回v
的地址符合慣例,但返回值本身可以被忽略。
和penalty
一樣,picksplit
函數(shù)對于索引的好性能至關(guān)重要。設(shè)計合適的penalty
和picksplit
是實現(xiàn)一個好的GiST索引中最大的挑戰(zhàn)。
same
如果兩個索引項相同則返回真,否則返回假(一個“索引項”是該索引的存儲類型的一個值,而不一定是原始被索引列類型的值)。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_same(storage_type, storage_type, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
在 C 模塊中匹配的代碼則應(yīng)該遵循這樣的框架:
PG_FUNCTION_INFO_V1(my_same);
Datum
my_same(PG_FUNCTION_ARGS)
{
prefix_range *v1 = PG_GETARG_PREFIX_RANGE_P(0);
prefix_range *v2 = PG_GETARG_PREFIX_RANGE_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
*result = my_eq(v1, v2);
PG_RETURN_POINTER(result);
}
由于歷史原因,same
函數(shù)不只返回一個布爾結(jié)果。相反它必須把該標志存儲在第三個參數(shù)指示的位置。盡管傳回該參數(shù)的地址符合慣例,但返回值本身可以被忽略。
distance
給定一個索引項p
和一個查詢值q
,這個函數(shù)決定兩者之間的“距離”。如果操作符類包含任何排序操作符,就必須提供這個函數(shù)。一個使用排序操作符的查詢將首先返回具有最小“距離”值的索引項,因此結(jié)果必須與操作符的語義一致。對于一個頁索引項,結(jié)果只表示到索引項的距離;對于一個內(nèi)部樹結(jié)點,結(jié)果必須是到任何子項的最小距離。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid, internal)
RETURNS float8
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
在 C 模塊中匹配的代碼則應(yīng)該遵循這樣的框架:
PG_FUNCTION_INFO_V1(my_distance);
Datum
my_distance(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
data_type *query = PG_GETARG_DATA_TYPE_P(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
/* Oid subtype = PG_GETARG_OID(3); */
/* bool *recheck = (bool *) PG_GETARG_POINTER(4); */
data_type *key = DatumGetDataType(entry->key);
double retval;
/*
* determine return value as a function of strategy, key and query.
*/
PG_RETURN_FLOAT8(retval);
}
distance
函數(shù)的參數(shù)和consistent
函數(shù)的相同。
在決定距離時允許有某種近似,只要結(jié)果不要超過該項的實際距離即可。因此,例如在幾何應(yīng)用中到一個外包盒的距離就足夠了。對于一個內(nèi)部樹節(jié)點,返回的距離不能超過到其任意一個子節(jié)點的距離。如果返回的距離不準確,該函數(shù)必須設(shè)置*recheck
為真(這對于內(nèi)部樹節(jié)點是不必要的,對于它們,計算總是被假定為不準確)。在這種情況下,執(zhí)行器將在從堆中取出元組后計算精確的距離,并且在必要時記錄這些元組。
如果距離函數(shù)對任意葉子節(jié)點都返回*recheck = true
,初始的排序操作符的返回類型必須是float8
或者float4
,并且距離函數(shù)的結(jié)果值必須能和初始排序操作符的結(jié)果進行比較,因為執(zhí)行器將使用距離函數(shù)結(jié)果和重新計算的排序操作符結(jié)果進行排序。否則,該距離函數(shù)的結(jié)果值可以是任意有限的float8
值,只要這些結(jié)果值的相對順序匹配該排序操作符返回的順序(在內(nèi)部會使用無窮以及負無窮來處理空值等情況,因此我們不推薦
distance
函數(shù)返回這些值)。
fetch
為只用索引的掃描將一個數(shù)據(jù)項壓縮過的索引表達轉(zhuǎn)換成原始的數(shù)據(jù)類型。被返回的數(shù)據(jù)必須是原始被索引值的一份準確的、非有損的拷貝。
該函數(shù)的SQL聲明必須看起來像這樣:
CREATE OR REPLACE FUNCTION my_fetch(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
參數(shù)是一個指向GISTENTRY
結(jié)構(gòu)的指針。在項上,它的key
域包含一個壓縮形式的非-NULL 葉子數(shù)據(jù)。返回值是另一個GISTENTRY
結(jié)構(gòu),其key
域包含同一數(shù)據(jù)的原始的未壓縮形式。如果操作符類的壓縮函數(shù)不對葉子項做任何事情,
fetch
方法可以原樣返回參數(shù)?;蛘撸绻搊pclass沒有一個壓縮函數(shù),則fetch
方法也可以被省略,因為它必須是一個空操作。
C 模塊中相應(yīng)的代碼可能會遵循下面的框架:
PG_FUNCTION_INFO_V1(my_fetch);
Datum
my_fetch(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
input_data_type *in = DatumGetPointer(entry->key);
fetched_data_type *fetched_data;
GISTENTRY *retval;
retval = palloc(sizeof(GISTENTRY));
fetched_data = palloc(sizeof(fetched_data_type));
/*
* 將 'fetched_data' 轉(zhuǎn)換成原始數(shù)據(jù)類型的一個 Datum。
*/
/* 從 fetched_data. 填充 *retval。 */
gistentryinit(*retval, PointerGetDatum(converted_datum),
entry->rel, entry->page, entry->offset, FALSE);
PG_RETURN_POINTER(retval);
}
如果該壓縮方法對于葉子項是有損的,操作符類就不能支持只用索引的掃描,并且不能定義fetch
函數(shù)。
選項
允許定義控制操作符類行為的用戶可見參數(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ù)訪問這些選項。
下面給出了my_options()和其他支持函數(shù)參數(shù)的示例實現(xiàn):
typedef enum MyEnumType
{
MY_ENUM_ON,
MY_ENUM_OFF,
MY_ENUM_AUTO
} MyEnumType;
typedef struct
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int int_param; /* integer parameter */
double real_param; /* real parameter */
MyEnumType enum_param; /* enum parameter */
int str_param; /* string parameter */
} MyOptionsStruct;
/* String representation of enum values */
static relopt_enum_elt_def myEnumValues[] =
{
{"on", MY_ENUM_ON},
{"off", MY_ENUM_OFF},
{"auto", MY_ENUM_AUTO},
{(const char *) NULL} /* list terminator */
};
static char *str_param_default = "default";
/*
* Sample validator: checks that string is not longer than 8 bytes.
*/
static void
validate_my_string_relopt(const char *value)
{
if (strlen(value) > 8)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("str_param must be at most 8 bytes")));
}
/*
* Sample filler: switches characters to lower case.
*/
static Size
fill_my_string_relopt(const char *value, void *ptr)
{
char *tmp = str_tolower(value, strlen(value), DEFAULT_COLLATION_OID);
int len = strlen(tmp);
if (ptr)
strcpy((char *) ptr, tmp);
pfree(tmp);
return len + 1;
}
PG_FUNCTION_INFO_V1(my_options);
Datum
my_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
init_local_reloptions(relopts, sizeof(MyOptionsStruct));
add_local_int_reloption(relopts, "int_param", "integer parameter",
100, 0, 1000000,
offsetof(MyOptionsStruct, int_param));
add_local_real_reloption(relopts, "real_param", "real parameter",
1.0, 0.0, 1000000.0,
offsetof(MyOptionsStruct, real_param));
add_local_enum_reloption(relopts, "enum_param", "enum parameter",
myEnumValues, MY_ENUM_ON,
"Valid values are: \"on\", \"off\" and \"auto\".",
offsetof(MyOptionsStruct, enum_param));
add_local_string_reloption(relopts, "str_param", "string parameter",
str_param_default,
&validate_my_string_relopt,
&fill_my_string_relopt,
offsetof(MyOptionsStruct, str_param));
PG_RETURN_VOID();
}
PG_FUNCTION_INFO_V1(my_compress);
Datum
my_compress(PG_FUNCTION_ARGS)
{
int int_param = 100;
double real_param = 1.0;
MyEnumType enum_param = MY_ENUM_ON;
char *str_param = str_param_default;
/*
* Normally, when opclass contains 'options' method, then options are always
* passed to support functions. However, if you add 'options' method to
* existing opclass, previously defined indexes have no options, so the
* check is required.
*/
if (PG_HAS_OPCLASS_OPTIONS())
{
MyOptionsStruct *options = (MyOptionsStruct *) PG_GET_OPCLASS_OPTIONS();
int_param = options->int_param;
real_param = options->real_param;
enum_param = options->enum_param;
str_param = GET_STRING_RELOPTION(options, str_param);
}
/* the rest implementation of support function */
}
由于GiST中的鍵表示是彈性的,它可能依賴于用戶指定的參數(shù)。 例如,可以指定密鑰簽名的長度。示例可參見gtsvector_options()
。
所有的 GiST 支持方法通常都在一個短暫存在的內(nèi)存上下文中被調(diào)用;就是說,每個元組被處理之后CurrentMemoryContext
將被重置。因此沒有必要操心釋放你 palloc 的所有東西。但是,在某些情況下,一個支持方法在重復(fù)調(diào)用之間緩存數(shù)據(jù)是有用的。要這樣做,將這些長期生存的數(shù)據(jù)分配在fcinfo->flinfo->fn_mcxt
中,并且在fcinfo->flinfo->fn_extra
中保持一個到它的指針。這種數(shù)據(jù)將在索引操作期間都存在(例如一次
GiST 索引掃描、索引構(gòu)建或索引元組插入)。注意當替換一個fn_extra
值時要釋放之前的值,否則在操作期間該泄露會累積。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: