64.3. Extensibility

GIN 接口具有很高的抽象度,要求访问方法实现者仅实现被访问数据类型的语义。 GIN 层本身负责并发,日志记录和搜索树结构。

要使 GIN 访问方法正常工作,要做的就是实现一些用户定义的方法,这些方法定义树中键的行为以及键,索引项和可索引查询之间的关系。简而言之,GIN 将可扩展性与通用性,代码重用和干净的接口结合在一起。

GIN 的运算符类必须提供两种方法:

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

    • 给定要索引的项目,返回一个由 palloc 分配的键数组。返回的键数必须存储在*nkeys中。如果任何键可以为 null,则还应分配一个*nkeys bool字段数组,将其地址存储在*nullFlags,并根据需要设置这些 null 标志。如果所有键都不为空,则*nullFlags可以保留为NULL(其初始值)。如果该项不包含键,则返回值可以为NULL
  • Datum *extractQuery(Datum query, int32 *nkeys, StrategyNumber n, bool **pmatch, Pointer **extra_data, bool **nullFlags, int32 *searchMode)

    • 返回给定要查询值的 palloc 分配的键数组;也就是说,query是可索引运算符右侧的值,可索引运算符的左侧是索引列。 n是操作员类别中的操作员的策略编号(请参阅Section 37.14.2)。 extractQuery通常需要咨询n以确定query的数据类型以及提取键值所使用的方法。返回的键数必须存储在*nkeys中。如果任何键可以为 null,则还应分配一个*nkeys bool字段数组,将其地址存储在*nullFlags,并根据需要设置这些 null 标志。如果所有键都不为空,则*nullFlags可以保留为NULL(其初始值)。如果query不包含任何键,则返回值为NULL

searchMode是一个输出参数,允许extractQuery指定有关搜索方式的详细信息。如果*searchMode设置为GIN_SEARCH_MODE_DEFAULT(这是它在调用前初始化的值),则只有与至少一个返回的键匹配的项才被视为候选匹配项。如果*searchMode设置为GIN_SEARCH_MODE_INCLUDE_EMPTY,则除了包含至少一个匹配键的项之外,根本不包含键的项也被视为候选匹配项。 (例如,此模式对于实现 is-subset-of 运算符很有用.)如果*searchMode设置为GIN_SEARCH_MODE_ALL,则索引中的所有非空项目都被视为候选匹配项,无论它们是否与返回的任何键匹配。 (此模式比其他两种选择慢得多,因为它本质上需要扫描整个索引,但是可能需要正确实现极端情况。在大多数情况下需要此模式的操作员可能不是 GIN 的理想选择access/gin.h中定义了用于设置此模式的符号。

pmatch是支持部分匹配的输出参数。要使用它,extractQuery必须分配一个*nkeys布尔数组并将其地址存储在*pmatch。如果相应的键要求部分匹配,则数组的每个元素应设置为 TRUE,否则设置为 FALSE。如果*pmatch设置为NULL,则 GIN 假定不需要部分匹配。变量在调用前被初始化为NULL,因此不支持部分匹配的运算符类可以忽略此参数。

extra_data是一个输出参数,它允许extractQuery将其他数据传递给consistentcomparePartial方法。要使用它,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。由于 GIN 不会显式存储项目,因此该函数无法直接访问索引项目的值。相反,可用的是关于从查询中提取的哪些键值出现在给定的索引项中的知识。 check数组的长度为nkeys,与extractQuery先前为此query数据返回的键数相同。如果索引项目包含相应的查询关键字,即,如果(check [i] == TRUE)索引项目中存在extractQuery结果数组的第 i 个关键字,则check数组的每个元素为 TRUE。万一consistent方法需要查询原始query数据,则传递该数据,先前由extractQuery返回的queryKeys[]nullFlags[]数组也将传递。 extra_dataextractQuery返回的额外数据数组,如果没有则返回NULL

extractQueryqueryKeys[]中返回空键时,如果索引的项目包含空键,则对应的check[]元素为 TRUE;也就是说,check[]的语义类似于IS NOT DISTINCT FROMconsistent函数可以检查相应的nullFlags[]元素是否需要确定常规值匹配和空匹配之间的区别。

成功后,如果需要根据查询运算符重新检查堆 Tuples,则*recheck应设置为 TRUE;如果索引测试正确,则应设置为 FALSE。也就是说,返回 FALSE 可以确保堆 Tuples 与查询不匹配。将*recheck设置为 FALSE 的 TRUE 返回值可确保堆 Tuples 确实与查询匹配; *recheck设置为 TRUE 的 TRUE 返回值意味着堆 Tuples 可能与查询匹配,因此需要通过直接针对原始索引项评估查询运算符来获取并重新检查它。

  • 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_FALSE,无论它是否包含GIN_MAYBE键。如果结果取决于GIN_MAYBE条目,即基于已知查询键无法确认或拒绝匹配,则该函数必须返回GIN_MAYBE

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

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

  • int compare(Datum a, Datum b)

    • 比较两个键(未索引的项!),并返回小于零,零或大于零的整数,指示第一个键是否小于,等于或大于第二个键。空键永远不会传递给此函数。

或者,如果运算符类未提供compare方法,则 GIN 将为索引键数据类型查找默认的 btree 运算符类,并使用其比较功能。建议在仅用于一种数据类型的 GIN 运算符类中指定比较函数,因为查找 btree 运算符类需要花费几个周期。但是,多态 GIN 运算符类(例如array_ops)通常不能指定单个比较函数。

(可选)GIN 的运算符类可以提供以下方法:

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

    • 比较部分匹配查询键和索引键。返回一个整数,其符号表示结果:小于零表示索引键与查询不匹配,但索引扫描应 continue;零表示索引键确实与查询匹配;大于零表示索引扫描应停止,因为不可能再进行匹配。如果需要其语义来确定何时结束扫描,则提供了生成部分匹配查询的操作员的策略编号n。此外,extra_dataextractQueryNULL(如果没有)组成的额外数据数组的对应元素。空键永远不会传递给此函数。

为了支持“部分匹配”查询,操作员类必须提供comparePartial方法,并且在遇到部分匹配查询时,其extractQuery方法必须设置pmatch参数。有关详情,请参见Section 64.4.2

上面提到的各种Datum值的实际数据类型取决于操作符类别。传递给extractValue的项目值始终是操作员类别的 Importing 类型,并且所有键值都必须属于该类别的STORAGE类型。传递给extractQueryconsistenttriConsistentquery参数的类型是由策略编号标识的类成员运算符的右侧 Importing 类型。只要可以从中提取正确类型的键值,就不必与索引类型相同。但是,建议这三个支持函数的 SQL 声明对query参数使用 opclass 的索引数据类型,即使实际的类型可能取决于操作符。