受支持版本: 16 / 15 / 14

65.3. 可扩展性 #

GIN 接口具有很高的抽象层次,访问方法实现者只需实现所访问数据类型的语义。 GIN 层本身会处理并发、日志记录以及树结构的搜索。

要让一个 GIN 访问方法工作起来,只需实现少数几个用户定义的方法, 它们定义了树中键的行为,以及键、被索引项和可索引查询之间的关系。简言之, GIN 将可扩展性与通用性、代码重用以及清晰的接口结合在一起。

GIN 操作符类必须提供两个方法:

Datum *extractValue(Datum itemValue, int32 *nkeys, bool **nullFlags) #

给定一个要建立索引的项,返回一个用 palloc 分配的键数组。返回键的数量必须存入 *nkeys。如果任一键可以为 null,还要另外 palloc 一个包含 *nkeysbool 字段的数组,将其地址存入 *nullFlags,并按需设置这些空值标志。如果所有键都非空, *nullFlags 可以保持为 NULL(其初始值)。 如果该项不包含任何键,则返回值可以为 NULL

Datum *extractQuery(Datum query, int32 *nkeys, StrategyNumber n, bool **pmatch, Pointer **extra_data, bool **nullFlags, int32 *searchMode) #

给定一个待查询的值,返回一个用 palloc 分配的键数组;也就是说, query 是一个可索引操作符右侧的值,而该操作符左侧是被索引列。 n 是该操作符在操作符类中的策略号(见 Section 36.16.2)。 通常,extractQuery 需要查看 n,以确定 query 的数据类型,以及应采用何种方法提取键值。返回键的数量必须存入 *nkeys。如果任一键可以为 null,还要另外 palloc 一个包含 *nkeysbool 字段的数组,将其地址存入 *nullFlags,并按需设置这些空值标志。如果所有键都非空, *nullFlags 可以保持为 NULL(其初始值)。 如果 query 不包含任何键,则返回值可以为 NULL

searchMode 是一个输出参数,用于让 extractQuery 指定搜索如何执行。若 *searchMode 被设置为 GIN_SEARCH_MODE_DEFAULT(调用前它会被初始化为该值), 则只有至少匹配一个返回键的项才会被视为候选匹配。若 *searchMode 被设置为 GIN_SEARCH_MODE_INCLUDE_EMPTY,则除至少包含一个匹配键的项之外, 完全不含任何键的项也会被视为候选匹配。(例如,该模式对于实现是子集操作符很有用。) 若 *searchMode 被设置为 GIN_SEARCH_MODE_ALL, 则索引中所有非空项都会被视为候选匹配,无论它们是否匹配任一返回键。 (该模式比前两种选择慢得多,因为它基本上需要扫描整个索引;但为了正确处理某些边界情况, 可能有此必要。在大多数情况下都需要此模式的操作符,大概并不适合作为 GIN 操作符类的候选。)用于设置该模式的符号定义在 access/gin.h 中。

pmatch 是一个在支持部分匹配时使用的输出参数。要使用它, extractQuery 必须分配一个包含 *nkeysbool 的数组,并将其地址存入 *pmatch。若相应键需要部分匹配, 则数组对应元素应设置为 true,否则设置为 false。如果 *pmatch 被设置为 NULL,那么 GIN 认为不需要部分匹配。 该变量在调用前会初始化为 NULL,因此不支持部分匹配的操作符类可以直接忽略此参数。

extra_data 是一个输出参数,用于让 extractQueryconsistentcomparePartial 方法传递额外数据。 要使用它,extractQuery 必须分配一个包含 *nkeys 个指针的数组,并将其地址存入 *extra_data,然后把所需内容存入各个指针。 该变量在调用前会初始化为 NULL,因此不需要额外数据的操作符类可以直接忽略此参数。 如果设置了 *extra_data,则整个数组会传给 consistent 方法,而对应元素会传给 comparePartial 方法。

操作符类还必须提供一个函数,用于检查被索引项是否匹配查询。它有两种形式:布尔型 consistent 函数,以及三值型 triConsistent 函数。 triConsistent 覆盖了两者的功能,因此仅提供 triConsistent 就已经足够。不过,如果布尔变体的计算明显更便宜,那么同时提供两者会更有利。 若只提供布尔变体,则一些依赖于在取回所有键之前先排除索引项的优化将被禁用。

bool consistent(bool check[], StrategyNumber n, Datum query, int32 nkeys, Pointer extra_data[], bool *recheck, Datum queryKeys[], bool nullFlags[]) #

如果被索引项满足策略号为 n 的查询操作符,则返回 true; 如果同时返回需要复核的指示,那么 true 也可以只表示它可能满足。 该函数无法直接访问被索引项的值,因为 GIN 并不显式存储项。 它所能利用的是这样一种信息:从查询中提取出的哪些键值出现在给定的被索引项中。 check 数组长度为 nkeys,这与先前针对该 query 数据由 extractQuery 返回的键数量相同。 如果被索引项包含相应查询键,则 check 数组中的对应元素为 true; 也就是说,如果 check[i] == true,则 extractQuery 结果数组中的第 i 个键存在于该被索引项中。传入原始 query 数据值, 是为了让 consistent 方法在需要时可以查看它;同样也会传入先前由 extractQuery 返回的 queryKeys[]nullFlags[] 数组。extra_data 则是 extractQuery 返回的额外数据数组,如果没有则为 NULL

extractQueryqueryKeys[] 中返回一个 null 键时, 若被索引项包含 null 键,则对应的 check[] 元素为 true;也就是说, check[] 的语义类似于 IS NOT DISTINCT FROM。 如果 consistent 函数需要区分普通值匹配与 null 匹配, 它可以检查对应的 nullFlags[] 元素。

成功时,如果需要根据查询操作符重新检查堆元组,则 *recheck 应设置为 true; 如果索引测试是精确的,则设置为 false。也就是说,返回 false 保证该堆元组不匹配查询; 返回 true 且 *recheck 为 false 保证该堆元组匹配查询; 返回 true 且 *recheck 为 true 则表示该堆元组可能匹配查询, 因此需要取出该元组,并直接对最初被索引的项值重新计算查询操作符以完成复核。

GinTernaryValue triConsistent(GinTernaryValue check[], StrategyNumber n, Datum query, int32 nkeys, Pointer extra_data[], Datum queryKeys[], bool nullFlags[]) #

triConsistentconsistent 类似,不过 check 向量中的元素不是布尔值,而是每个键都有三种可能的值: GIN_TRUEGIN_FALSEGIN_MAYBEGIN_FALSEGIN_TRUE 与普通布尔值含义相同, 而 GIN_MAYBE 表示该键是否存在尚不确定。存在 GIN_MAYBE 值时,只有当无论索引项是否包含对应查询键,该项都确定匹配时,函数才应返回 GIN_TRUE。同样,只有当无论是否包含 GIN_MAYBE 键, 该项都确定不匹配时,函数才必须返回 GIN_FALSE。如果结果依赖于 GIN_MAYBE 条目,也就是说,无法根据已知的查询键确认或否定匹配, 则函数必须返回 GIN_MAYBE

check 向量中没有 GIN_MAYBE 值时, 返回 GIN_MAYBE 等价于在布尔型 consistent 函数中设置 recheck 标志。

此外,GIN 必须有一种方法对存储在索引中的键值进行排序。 操作符类可以通过指定一个比较方法来定义这种排序顺序:

int compare(Datum a, Datum b) #

比较两个键(不是被索引项!),并返回一个整数:小于零、等于零或大于零, 分别表示第一个键小于、等于或大于第二个键。null 键绝不会被传递给这个函数。

或者,如果操作符类没有提供 compare 方法,GIN 将查找该索引键数据类型的默认 B-树操作符类,并使用其比较函数。建议在仅面向单一数据类型的 GIN 操作符类中显式指定比较函数,因为查找 B-树操作符类会消耗少量周期。 不过,多态 GIN 操作符类(例如 array_ops)通常无法指定单一比较函数。

用于 GIN 的操作符类还可以选择性提供下列方法:

int comparePartial(Datum partial_key, Datum key, StrategyNumber n, Pointer extra_data) #

比较部分匹配查询键与索引键。返回一个整数,其符号表示结果:小于零表示索引键不匹配查询, 但索引扫描应继续;零表示索引键匹配查询;大于零表示索引扫描应停止,因为后续不可能再有匹配项。 产生该部分匹配查询的操作符的策略号 n 会被传入,以便在需要其语义来决定何时结束扫描时使用。 另外,extra_data 是由 extractQuery 生成的额外数据数组中的对应元素, 如果没有则为 NULL。null 键绝不会被传递给这个函数。

void options(local_relopts *relopts) #

定义一组用户可见的参数,用于控制操作符类行为。

options 函数会接收一个指向 local_relopts 结构的指针,需要用一组特定于操作符类的选项填充它。这些选项可通过 PG_HAS_OPCLASS_OPTIONS()PG_GET_OPCLASS_OPTIONS() 宏,在其他支持函数中访问。

由于被索引值的键提取方式以及键在 GIN 中的表示都很灵活, 它们可能依赖用户指定的参数。

要支持部分匹配查询,操作符类必须提供 comparePartial 方法, 并且其 extractQuery 方法在遇到部分匹配查询时必须设置 pmatch 参数。详见 Section 65.4.2

上文提到的各种 Datum 值,其实际数据类型会因操作符类而异。 传给 extractValue 的项值始终是该操作符类的输入类型,而所有键值都必须是该类的 STORAGE 类型。传给 extractQueryconsistenttriConsistentquery 参数类型,是由该策略号标识的类成员操作符右侧输入类型。 只要能够从中提取出正确类型的键值,它就不必与被索引类型相同。不过,建议在这三个支持函数的 SQL 声明中,对 query 参数使用该操作符类的被索引数据类型, 尽管依据具体操作符,实际类型可能是别的类型。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。