# 66.3.
可扩展性SP-GiST 提供了一个高级抽象的接口,要求访问方法开发人员只实现特定于给定数据类型的方法。SP-GiST 核心负责高效的磁盘映射和树结构搜索。
它还负责并发和日志记录注意事项。SP-GiST 树的叶元组通常包含与索引列具有相同数据类型的值,尽管它们也可能包含索引列的有损表示。存储在根级别的叶元组将直接表示原始索引数据值,但较低级别的叶元组可能仅包含部分值,例如后缀。
在这种情况下,操作符类支持函数必须能够使用从内部元组中积累的信息来重建原始值,这些内部元组通过这些信息到达叶级。当使用创建 SP-GiST 索引时
包括列,这些列的值也存储在叶元组中。这
包括
列与 SP-GiST 运算符类无关,因此这里不再进一步讨论。内部元组更复杂,因为它们是搜索树中的分支点。每个内部元组包含一组一个或多个节点,代表相似叶值的组。一个节点包含一个下行链路,该下行链路要么通向另一个较低级别的内部元组,要么通向一个简短的叶元组列表,这些叶元组都位于同一索引页面上。每个节点通常有一个标签描述它;例如,在基数树中,节点标签可以是字符串值的下一个字符。(或者,一个操作符类可以省略节点标签,如果它与所有内部元组的一组固定节点一起工作;参见第 66.4.2 节.) 可选地,一个内部元组可以有一个字首描述其所有成员的值。在基数树中,这可能是表示字符串的公共前缀。前缀值不一定是真正的前缀,可以是算子类需要的任何数据;例如,在四叉树中,它可以存储测量四个象限的中心点。然后,四叉树内部元组也将包含四个节点,这些节点对应于该中心点周围的象限。
一些树算法需要了解当前元组的级别(或深度),因此 SP-GiST 核心为运算符类提供了在下降树时管理级别计数的可能性。还支持在需要时增量重建表示的值,以及传递额外的数据(称为遍历值) 在树下降期间。
# 笔记
SP-GiST 核心代码负责处理空条目。尽管 SP-GiST 索引确实在索引列中存储空值条目,但这对索引运算符类代码是隐藏的:不会将空索引条目或搜索条件传递给运算符类方法。(假设 SP-GiST 运算符是严格的,因此对于空值不能成功。)因此这里不进一步讨论空值。
SP-GiST 的索引运算符类必须提供五种用户定义的方法,其中两种是可选的。所有五种强制方法都遵循接受两种的约定内部的
参数,第一个参数是指向包含支持方法的输入值的 C 结构的指针,而第二个参数是指向必须放置输出值的 C 结构的指针。四个强制方法只返回空白
,因为它们的所有结果都出现在输出结构中;但叶一致
返回一个布尔值
结果。这些方法不得修改其输入结构的任何字段。在所有情况下,输出结构都在调用用户定义的方法之前初始化为零。可选的第六种方法压缩
接受一个基准
被索引为唯一的参数,并返回一个适合物理存储在叶元组中的值。可选的第七种方法选项
接受一个内部的
指向 C 结构的指针,其中应该放置 opclass 特定的参数,并返回空白
.
五个强制性的用户定义方法是:
配置
返回有关索引实现的静态信息,包括前缀和节点标签数据类型的数据类型 OID。
函数的 SQL 声明必须如下所示:
CREATE FUNCTION my_config(internal, internal) RETURNS void ...
第一个参数是一个指向spgConfigIn
C 结构,包含函数的输入数据。第二个参数是一个指向spgConfigOut
C 结构,函数必须用结果数据填充该结构。
typedef struct spgConfigIn
{
Oid attType; /* Data type to be indexed */
} spgConfigIn;
typedef struct spgConfigOut
{
Oid prefixType; /* Data type of inner-tuple prefixes */
Oid labelType; /* Data type of inner-tuple node labels */
Oid leafType; /* Data type of leaf-tuple values */
bool canReturnData; /* Opclass can reconstruct original data */
bool longValuesOK; /* Opclass can cope with values > 1 page */
} spgConfigOut;
属性类型
被传递以支持多态索引运算符类;对于普通的固定数据类型运算符类,它总是具有相同的值,因此可以忽略。
对于不使用前缀的运算符类,前缀类型
可以设置为虚空
.同样,对于不使用节点标签的运算符类,标签类型
可以设置为虚空
.可以返回数据
如果操作符类能够重建最初提供的索引值,则应该设置为真。长值确定
只有当属性类型
是可变长度的,并且操作符类能够通过重复后缀来分割长值(参见第 66.4.1 节)。
叶型
应该与操作符类定义的索引存储类型相匹配opckeytype
目录条目。(注意opckeytype
可以为零,表示存储类型与运算符类的输入类型相同,这是最常见的情况。)出于向后兼容的原因,配置
方法可以设置叶型
到某个其他值,并且将使用该值;但这已被弃用,因为随后在目录中错误地标识了索引内容。另外,允许离开叶型
未初始化(零);这被解释为意味着索引存储类型派生自opckeytype
.
什么时候属性类型
和叶型
不同,可选方法压缩
必须提供。方法压缩
负责转换要索引的基准属性类型
到叶型
.
选择
选择一种将新值插入内部元组的方法。
函数的 SQL 声明必须如下所示:
CREATE FUNCTION my_choose(internal, internal) RETURNS void ...
第一个参数是一个指向spgChooseIn
C 结构,包含函数的输入数据。第二个参数是一个指向spgChooseOut
C 结构,函数必须用结果数据填充该结构。
typedef struct spgChooseIn
{
Datum datum; /* original datum to be indexed */
Datum leafDatum; /* current datum to be stored at leaf */
int level; /* current level (counting from zero) */
/* Data from current inner tuple */
bool allTheSame; /* tuple is marked all-the-same? */
bool hasPrefix; /* tuple has a prefix? */
Datum prefixDatum; /* if so, the prefix value */
int nNodes; /* number of nodes in the inner tuple */
Datum *nodeLabels; /* node label values (NULL if none) */
} spgChooseIn;
typedef enum spgChooseResultType
{
spgMatchNode = 1, /* descend into existing node */
spgAddNode, /* add a node to the inner tuple */
spgSplitTuple /* split inner tuple (change its prefix) */
} spgChooseResultType;
typedef struct spgChooseOut
{
spgChooseResultType resultType; /* action code, see above */
union
{
struct /* results for spgMatchNode */
{
int nodeN; /* descend to this node (index from 0) */
int levelAdd; /* increment level by this much */
Datum restDatum; /* new leaf datum */
} matchNode;
struct /* results for spgAddNode */
{
Datum nodeLabel; /* new node's label */
int nodeN; /* where to insert it (index from 0) */
} addNode;
struct /* results for spgSplitTuple */
{
/* Info to form new upper-level inner tuple with one child tuple */
bool prefixHasPrefix; /* tuple should have a prefix? */
Datum prefixPrefixDatum; /* if so, its value */
int prefixNNodes; /* number of nodes */
Datum *prefixNodeLabels; /* their labels (or NULL for
* no labels) */
int childNodeN; /* which node gets child tuple */
/* Info to form new lower-level inner tuple with all old nodes */
bool postfixHasPrefix; /* tuple should have a prefix? */
Datum postfixPrefixDatum; /* if so, its value */
} splitTuple;
} result;
} spgChooseOut;
基准
是原始数据spgConfigIn
.属性类型
要插入索引的类型。叶基准
是一个值spgConfigOut
.叶型
类型,最初是方法的结果压缩
应用于基准
当方法压缩
提供,或相同的值基准
除此以外。叶基准
可以在树的较低级别更改,如果选择
或者分拣
方法改变它。当插入搜索到达叶子页时,当前值为叶基准
是将存储在新创建的叶元组中的内容。等级
是当前内部元组的级别,从根级别的零开始。都一样
如果当前内部元组被标记为包含多个等效节点,则为真(参见第 66.4.3 节)。有前缀
如果当前内部元组包含前缀,则为真;如果是这样的话,前缀基准
是它的价值。节点
是内部元组中包含的子节点的数量,并且节点标签
是其标签值的数组,如果没有标签,则为NULL。
这个选择
函数可以确定新值是否与现有子节点之一匹配,或者必须添加新的子节点,或者新值是否与元组前缀不一致,因此必须拆分内部元组以创建限制较少的前缀。
如果新值与现有子节点之一匹配,则设置结果类型
到spgMatchNode
设置诺登
到节点数组中该节点的索引(从零开始)。设置levelAdd
增加数量
如果运算符类不使用级别,则将其保留为零。设置重新基准
相等叶数据
如果操作员类没有将基准从一个级别修改到下一个级别,或以其他方式将其设置为要用作叶数据
在下一个层面。
如果必须添加新的子节点,请设置结果类型
到spgAddNode
设置诺德拉贝尔
要用于新节点的标签,并设置诺登
到要在节点数组中插入节点的索引(从零开始)。添加节点后选择
函数将使用修改后的内部元组再次调用;这个电话应该会导致spgMatchNode
后果
如果新值与元组前缀不一致,请设置结果类型
到spgSplitTuple
。此操作将所有现有节点移动到一个新的较低级别内部元组中,并将现有内部元组替换为一个具有指向新的较低级别内部元组的单个下行链路的元组。设置前缀
指示新的上层元组是否应该有前缀,如果设置了前缀预膨胀
指向前缀值。此新前缀值的限制性必须比原始值小得多,才能接受要编制索引的新值。设置前缀节点
到新元组中所需的节点数,并设置前缀节点标号
指向包含标签的palloc数组,或者如果不需要节点标签,则为NULL。请注意,新的上层元组的总大小不得超过它要替换的元组的总大小;这将限制新前缀和新标签的长度。设置childNodeN
到将下行链路到新的较低级别内部元组的节点的索引(从零开始)。设置后缀前缀
指示新的较低级别内部元组是否应具有前缀,如果设置了前缀后固定
指向前缀值。这两个前缀和下行链路节点标签(如果有)的组合必须与原始前缀具有相同的含义,因为没有机会更改移动到新的较低级别元组的节点标签,也没有机会更改任何子索引项。分割节点后选择
函数将使用替换的内部元组再次调用。这个电话可能会回电spgAddNode
结果,如果spgSplitTuple
行动最后选择
必须回来spgMatchNode
允许插入下降到下一个级别。
皮克斯普利特
决定如何在一组叶元组上创建新的内部元组。
函数的SQL声明必须如下所示:
CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ...
第一个参数是指向spgPickSplitIn
C结构,包含函数的输入数据。第二个参数是指向spgPickSplitOut
C结构,函数必须用结果数据填充该结构。
typedef struct spgPickSplitIn
{
int nTuples; /* number of leaf tuples */
Datum *datums; /* their datums (array of length nTuples) */
int level; /* current level (counting from zero) */
} spgPickSplitIn;
typedef struct spgPickSplitOut
{
bool hasPrefix; /* new inner tuple should have a prefix? */
Datum prefixDatum; /* if so, its value */
int nNodes; /* number of nodes for new inner tuple */
Datum *nodeLabels; /* their labels (or NULL for no labels) */
int *mapTuplesToNodes; /* node index for each leaf tuple */
Datum *leafTupleDatums; /* datum to store in each new leaf tuple */
} spgPickSplitOut;
N倍
是提供的叶元组数。基准
是它们的基准值的数组SPGConfiguut
.叶型
类型数量
是所有叶元组共享的当前级别,它将成为新的内部元组的级别。
设置hasPrefix
指示新的内部元组是否应该有前缀,如果设置了前缀前驱体
指向前缀值。设置n节点
指示新内部元组将包含的节点数,并设置诺德拉贝尔斯
设置为其标签值的数组,如果不需要节点标签,则设置为NULL。设置mapTuplesToNodes
指向一个数组,该数组给出每个叶元组应分配给的节点的索引(从零开始)。设置叶元宝
存储在新叶元组中的值数组(这些值与输入值相同)基准
如果操作员类没有将基准从一个级别修改到下一个级别)。请注意皮克斯普利特
职能部门负责将诺德拉贝尔斯
, mapTuplesToNodes
和叶元宝
数组。
如果提供了多个叶元组,则皮克斯普利特
函数将它们分为多个节点;否则,不可能在多个页面上拆分叶元组,这是此操作的最终目的。因此,如果皮克斯普利特
函数最终将所有叶元组放在同一个节点中,核心SP GiST代码将覆盖该决定,并生成一个内部元组,其中叶元组随机分配给几个具有相同标签的节点。这样的元组是有标记的照样
表明这已经发生。这个选择
和内部一致
函数必须适当注意这样的内部元组。看见第66.4.3节了解更多信息。
皮克斯普利特
仅当配置
函数集朗瓦卢索克
设置为true,并提供了一个大于一页的输入值。在这种情况下,操作的要点是去掉前缀并生成一个新的、较短的叶基准值。将重复调用,直到生成足够短的叶子数据,以适合页面。看见第66.4.1节了解更多信息。
内部一致
返回树搜索期间要遵循的节点集(分支)。
函数的SQL声明必须如下所示:
CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...
第一个参数是指向spgInnerConsistentIn
C结构,包含函数的输入数据。第二个参数是指向spgInnerConsistentOut
C结构,函数必须用结果数据填充该结构。
typedef struct spgInnerConsistentIn
{
ScanKey scankeys; /* array of operators and comparison values */
ScanKey orderbys; /* array of ordering operators and comparison
* values */
int nkeys; /* length of scankeys array */
int norderbys; /* length of orderbys array */
Datum reconstructedValue; /* value reconstructed at parent */
void *traversalValue; /* opclass-specific traverse value */
MemoryContext traversalMemoryContext; /* put new traverse values here */
int level; /* current level (counting from zero) */
bool returnData; /* original data must be returned? */
/* Data from current inner tuple */
bool allTheSame; /* tuple is marked all-the-same? */
bool hasPrefix; /* tuple has a prefix? */
Datum prefixDatum; /* if so, the prefix value */
int nNodes; /* number of nodes in the inner tuple */
Datum *nodeLabels; /* node label values (NULL if none) */
} spgInnerConsistentIn;
typedef struct spgInnerConsistentOut
{
int nNodes; /* number of child nodes to be visited */
int *nodeNumbers; /* their indexes in the node array */
int *levelAdds; /* increment level by this much for each */
Datum *reconstructedValues; /* associated reconstructed values */
void **traversalValues; /* opclass-specific traverse values */
double **distances; /* associated distances */
} spgInnerConsistentOut;
阵列扫描键
,长度肯基
,描述索引搜索条件。这些条件与和结合在一起——只有满足所有条件的索引项才是有趣的。(注意肯基
=0表示所有索引项都满足查询。)通常,一致性函数只关心sk_战略
和sk_论点
每个数组项的字段,分别给出可索引运算符和比较值。尤其是不需要检查sk_旗
查看比较值是否为空,因为SP GiST核心代码将过滤掉此类条件。阵列订货人
,长度诺德比
,以相同的方式描述排序运算符(如果有)。重建值
是为父元组重构的值;它是(基准)0
在根级别,或者如果内部一致
函数未在父级提供值。遍历值
是指向从上一次调用内部一致
在父索引元组上,或在根级别为NULL。traversalMemoryContext
存储输出遍历值的内存上下文(见下文)。数量
是当前内部元组的级别,根级别从零开始。返回数据
是符合事实的
如果此查询需要重建数据;只有当配置
函数断言返回数据
. 照样
如果当前内部元组标记为“所有相同”,则为true;在这种情况下,所有节点都有相同的标签(如果有的话),因此它们要么全部匹配,要么没有匹配查询(请参阅)第66.4.3节). hasPrefix
如果当前内部元组包含前缀,则为true;如果是这样,前驱体
这就是它的价值所在。n节点
是内部元组中包含的子节点数,以及诺德拉贝尔斯
是其标签值的数组,如果节点没有标签,则为NULL。
n节点
必须设置为搜索需要访问的子节点数,以及点名者
必须设置为它们的索引数组。如果操作员类跟踪级别,则设置levelAdds
下降到要访问的每个节点时所需的级别增量数组。(通常所有节点的增量都相同,但不一定如此,所以使用数组。)如果需要重建值,请设置重建值
为要访问的每个子节点重建的值数组;否则,请离开重建值
为空。重建的值被假定为SPGConfiguut
.叶型
(然而,由于核心系统除了可能复制它们之外,不会对它们进行任何处理,因此它们拥有相同的功能就足够了。)泰普伦
和typbyval
属性为叶型
)如果执行了有序搜索,则设置距离
根据订货人
数组(首先处理距离最小的节点)。否则将其保留为空。如果需要将额外的带外信息(“遍历值”)传递到树搜索的较低级别,请设置遍历值
到一个适当的遍历值数组,每个要访问的子节点对应一个遍历值;否则,请离开遍历值
为空。请注意内部一致
职能部门负责将点名者
, levelAdds
, 距离
, 重建值
和遍历值
当前内存上下文中的数组。但是,任何由遍历值
数组应该在traversalMemoryContext
.每个遍历值必须是一个palloc'd块。
叶_一致
如果叶元组满足查询,则返回true。
函数的SQL声明必须如下所示:
CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...
第一个参数是指向spgLeafConsistentIn
C结构,包含函数的输入数据。第二个参数是指向spgLeafConsistentOut
C结构,函数必须用结果数据填充该结构。
typedef struct spgLeafConsistentIn
{
ScanKey scankeys; /* array of operators and comparison values */
ScanKey orderbys; /* array of ordering operators and comparison
* values */
int nkeys; /* length of scankeys array */
int norderbys; /* length of orderbys array */
Datum reconstructedValue; /* value reconstructed at parent */
void *traversalValue; /* opclass-specific traverse value */
int level; /* current level (counting from zero) */
bool returnData; /* original data must be returned? */
Datum leafDatum; /* datum in leaf tuple */
} spgLeafConsistentIn;
typedef struct spgLeafConsistentOut
{
Datum leafValue; /* reconstructed original data, if any */
bool recheck; /* set true if operator must be rechecked */
bool recheckDistances; /* set true if distances must be rechecked */
double *distances; /* associated distances */
} spgLeafConsistentOut;
阵列扫描键
,长度肯基
,描述索引搜索条件。这些条件与和结合在一起,只有满足所有条件的索引项才能满足查询。(注意肯基
=0表示所有索引项都满足查询。)通常,一致性函数只关心sk_战略
和sk_论点
每个数组项的字段,分别给出可索引运算符和比较值。尤其是不需要检查sk_旗
查看比较值是否为空,因为SP GiST核心代码将过滤掉此类条件。阵列订货人
,长度诺德比
,以相同的方式描述排序运算符。重建值
是为父元组重构的值;它是(基准)0
在根级别,或者如果内部一致
函数未在父级提供值。遍历值
是指向从上一次调用内部一致
在父索引元组上,或在根级别为NULL。数量
是当前叶元组的级别,根级别从零开始。返回数据
是符合事实的
如果此查询需要重建数据;只有当配置
函数断言返回数据
. 叶数据
它的关键价值是什么SPGConfiguut
.叶型
存储在当前叶元组中。
函数必须返回符合事实的
如果叶元组与查询匹配,或者错误的
如果不是的话。在符合事实的
如果返回数据
是符合事实的
然后叶价值
必须设置为(类型)的值spgConfigIn
.attType
)最初提供此叶元组的索引。而且复查
可能设置为符合事实的
如果匹配不确定,则必须将运算符重新应用于实际堆元组以验证匹配。如果执行了有序搜索,则设置距离
根据订货人
大堆否则将其保留为空。如果返回的距离中至少有一个不准确,则设置重新计算距离
这是真的。在这种情况下,执行器将在从堆中获取元组后计算确切的距离,并在需要时对元组重新排序。
可选的用户定义方法包括:
基准压缩(基准输入)
将数据项转换为适合在索引的叶元组中进行物理存储的格式。它接受类型为的值spgConfigIn
.attType
并返回类型为的值SPGConfiguut
.叶型
.输出值不得包含不符合要求的TOAST指针。
注:该压紧
方法仅应用于要存储的值。一致性方法接收查询扫描键
未更改,无需使用压紧
.
选项
定义一组控制运算符类行为的用户可见参数。
函数的SQL声明必须如下所示:
CREATE OR REPLACE FUNCTION my_options(internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
函数被传递一个指向本地重新选择
struct,它需要填充一组特定于运算符类的选项。可以使用从其他支持功能访问这些选项PG_有_OPCLASS_选项()
和PG_GET_OPCLASS_OPTIONS()
宏。
由于SP GiST中密钥的表示是灵活的,因此它可能取决于用户指定的参数。
所有SP GiST支持方法通常在短期内存上下文中调用;就是,CurrentMemoryContext
将在处理每个元组后重置。因此,担心自己失去的一切并不重要。(小标题)配置
方法是一个例外:它应该尽量避免内存泄漏。但通常情况下配置
方法只需将常量赋给传递的参数结构。)
如果索引列是可折叠的数据类型,则索引排序规则将使用标准PG_GET_COLLATION()
机械装置