如 表 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 bool
val
和 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 数据类型时,由于输入上 TOAST 压缩的不一致应用,两个图像相等的数据的磁盘表示可能在按位上不相等。正式地,当操作符类的 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 树的进一步演变中找到用途。