67.3. B 树支持函数#
如表 38.9所示,btree 定义了一个必需的支持函数和四个可选的支持函数。五种用户定义的方法是
order
对于 btree 运算符族为其提供比较运算符的每种数据类型组合,它必须提供一个比较支持函数,在
pg_amproc
中注册,支持函数编号为 1,amproclefttype
/amprocrighttype
等于比较的左右数据类型(即,与匹配运算符在pg_amop
中注册的相同数据类型)。比较函数必须采用两个非空值A
和B
,并返回一个int32
值,当A
<
B
、A
=
B
或A
>
B
时,该值分别为<
0
、0
或>
0
。不允许出现空结果:数据类型的所有值都必须可比较。有关示例,请参阅src/backend/access/nbtree/nbtcompare.c
。如果比较的值是可整理数据类型,则使用标准
PG_GET_COLLATION()
机制将适当的整理 OID 传递给比较支持函数。排序支持
B 树运算符系列还可以选择提供在支持函数编号 2 下注册的排序支持函数。这些函数允许以比天真地调用比较支持函数更有效的方式实现排序目的的比较。涉及的 API 在
src/include/utils/sortsupport.h
中定义。in_range
B 树运算符系列还可以选择提供在支持函数编号 3 下注册的in_range支持函数。这些函数在 B 树索引操作期间不会使用;相反,它们扩展了运算符系列的语义,以便它可以支持包含
RANGE
offset
PRECEDING
和RANGE
offset
FOLLOWING
帧边界类型的窗口子句(请参阅 第 4.2.8 节)。从根本上讲,提供的信息是如何以与系列数据排序兼容的方式添加或减去offset
值。in_range
函数必须具有签名in_range(
val
type1,base
type1,offset
type2,sub
bool,less
bool) returns boolval
和base
必须具有相同的类型,该类型是运算符系列支持的类型之一(即它提供排序的类型)。但是,offset
可能是不同类型的,这可能是系列原本不支持的类型。一个示例是内置的time_ops
系列提供了一个in_range
函数,其offset
类型为interval
。系列可以为其任何受支持的类型和一个或多个offset
类型提供in_range
函数。每个in_range
函数都应输入pg_amproc
,其中amproclefttype
等于type1
,amprocrighttype
等于type2
。in_range
函数的基本语义取决于两个布尔标志参数。它应添加或减去base
和offset
,然后将val
与结果进行比较,如下所示如果
!
sub
且!
less
,返回val
>=
(base
+
offset
)如果
!
sub
且less
,返回val
<=
(base
+
offset
)如果
sub
且!
less
,返回val
>=
(base
-
offset
)如果
sub
且less
,返回val
<=
(base
-
offset
)
在执行此操作之前,该函数应检查
offset
的符号:如果小于零,则引发错误ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE
(22013),并显示类似 “窗口函数中无效的前后大小” 的错误文本。(SQL 标准要求这样做,尽管非标准运算符系列可能会选择忽略此限制,因为似乎没有太大的语义必要性。)此要求委派给in_range
函数,以便核心代码不必理解 “小于零” 对特定数据类型意味着什么。另一个期望是,如果可行,
in_range
函数应避免在base
+
offset
或base
-
offset
溢出时引发错误。即使该值超出数据类型的范围,也可以确定正确的比较结果。请注意,如果数据类型包含 “无穷大” 或 “NaN” 等概念,则可能需要格外小心,以确保in_range
的结果与运算符系列的正常排序顺序一致。in_range
函数的结果必须与运算符系列施加的排序顺序一致。确切地说,给定offset
和sub
的任何固定值,则如果
less
= true 的in_range
对某些val1
和base
为真,则它必须对具有相同base
的每个val2
<=
val1
为真。如果
in_range
与less
= true 对于某些val1
和base
为假,则它对于每个val2
>=
val1
与相同的base
都必须为假。如果
in_range
与less
= true 对于某些val
和base1
为真,则它对于每个base2
>=
base1
与相同的val
都必须为真。如果
in_range
与less
= true 对于某些val
和base1
为假,则它对于每个base2
<=
base1
与相同的val
都必须为假。
当
less
= false 时,具有反转条件的类似语句成立。如果正在排序的类型 (
type1
) 是可排序的,则将使用标准 PG_GET_COLLATION() 机制将适当的排序 OID 传递给in_range
函数。in_range
函数无需处理 NULL 输入,并且通常会被标记为严格。equalimage
或者,btree 运算符族可以提供
equalimage
(“相等暗示图像相等”) 支持函数,在支持函数编号 4 下注册。这些函数允许核心代码确定何时可以安全地应用 btree 重复数据删除优化。目前,仅在构建或重建索引时才调用equalimage
函数。equalimage
函数必须具有签名equalimage(
opcintype
oid
) returns bool返回值是有关运算符类和排序规则的静态信息。返回
true
表示运算符类的order
函数保证仅在A
和B
参数在不丢失任何语义信息的情况下也可以互换时才返回0
(“参数相等”)。不注册equalimage
函数或返回false
表示无法保证此条件成立。opcintype
参数是运算符类索引的数据类型的
。这是一种便利,允许在运算符类中重复使用相同的底层pg_type
.oidequalimage
函数。如果opcintype
是可排序的数据类型,则将使用标准PG_GET_COLLATION()
机制将适当的排序 OID 传递给equalimage
函数。就操作符类而言,返回
true
表示重复数据删除是安全的(或者对于其equalimage
函数传递 OID 的校对是安全的)。但是,核心代码仅当每个索引列使用注册了equalimage
函数的操作符类,并且每个函数在调用时实际上返回true
时,才认为重复数据删除对于索引是安全的。图像相等与简单的按位相等条件几乎相同。有一个细微差别:在索引 varlena 数据类型时,由于输入上 压缩的不一致应用,两个图像相等的数据的磁盘表示可能在按位上不相等。正式地,当操作符类的
equalimage
函数返回true
时,可以安全地假设datum_image_eq()
C 函数将始终与操作符类的order
函数一致(前提是将相同的校对 OID 传递给equalimage
和order
函数)。核心代码从根本上无法根据同一系列中其他操作符类的详细信息推断出多数据类型系列中操作符类的“相等意味着图像相等”状态。此外,操作符系列注册跨类型
equalimage
函数是不明智的,并且尝试这样做会导致错误。这是因为“相等意味着图像相等”状态不仅取决于排序/相等语义,这些语义或多或少在操作符系列级别定义。通常,必须单独考虑一种特定数据类型实现的语义。核心 PostgreSQL 发行版中包含的操作符类遵循的惯例是注册一个现成的通用
equalimage
函数。大多数操作符类注册btequalimage()
,它表示重复数据删除是无条件安全的。诸如text
之类的可整理数据类型操作符类注册btvarstrequalimage()
,它表示重复数据删除对于确定性校对是安全的。第三方扩展的最佳做法是注册自己的自定义函数以保留控制权。选项
B 树操作符系列可以选择提供
options
(“操作符类特定选项”)支持函数,在支持函数编号 5 下注册。这些函数定义了一组用户可见参数,用于控制操作符类行为。一个
options
支持函数必须具有签名options(
relopts
local_relopts *
) returns void该函数传递给
local_relopts
结构的指针,该结构需要用一组操作符类特定选项填充。可以使用PG_HAS_OPCLASS_OPTIONS()
和PG_GET_OPCLASS_OPTIONS()
宏从其他支持函数访问这些选项。目前,没有 B 树操作符类具有
options
支持函数。B 树不允许像 GiST、SP-GiST、GIN 和 BRIN 那样灵活地表示键。因此,options
可能在当前 B 树索引访问方法中没有太多应用。然而,此支持函数被添加到 B 树中以实现统一,并且可能会在 PostgreSQL 中 B 树的进一步演变中找到用途。