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_rangeB 树运算符系列还可以选择提供在支持函数编号 3 下注册的in_range支持函数。这些函数在 B 树索引操作期间不会使用;相反,它们扩展了运算符系列的语义,以便它可以支持包含
RANGEoffsetPRECEDING和RANGEoffsetFOLLOWING帧边界类型的窗口子句(请参阅 第 4.2.8 节)。从根本上讲,提供的信息是如何以与系列数据排序兼容的方式添加或减去offset值。in_range函数必须具有签名in_range(
valtype1,basetype1,offsettype2,subbool,lessbool) 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(
opcintypeoid) 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(
reloptslocal_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 树的进一步演变中找到用途。
