pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
现在我们有了一个访问方法和一个操作符类。我们还需要一组操作符。定义操作符的过程已在第 14 章中讨论过。对于 B-树上的 complex_abs_ops 操作符类,我们需要的操作符是:
假设实现这些函数的代码存储在文件 中, 并且我们已把它编译成 PGROOT/src/tutorial/complex.c。 C 代码的一部分如下所示:PGROOT/src/tutorial/complex.so
#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y)
bool
complex_abs_eq(Complex *a, Complex *b)
{
double amag = Mag(a), bmag = Mag(b);
return (amag==bmag);
}
(注意,在余下的示例中我们只展示相等操作符。其他四个操作符非常类似。 细节请参阅 complex.c 或 complex.source。)
我们像下面这样让 PostgreSQL 知道该函数:
CREATE FUNCTION complex_abs_eq(complex, complex) RETURNS boolean
AS 'PGROOT/src/tutorial/complex'
LANGUAGE C;
这里有几件重要的事情:
首先,注意这里正在为 complex 定义小于、小于等于、等于、 大于等于和大于操作符。我们只能有一个名为(例如)= 并且两个操作数都取 complex 类型的操作符。在本例中我们没有其他用于 complex 的 = 操作符,但如果我们在构造一种实用的数据类型, 我们可能希望 = 是复数的普通相等操作。在那种情况下,我们就需要为 complex_abs_eq 使用其他某个操作符名。
其次,尽管 PostgreSQL 能处理名称相同但输入数据类型 不同的操作符,C 却只能处理给定名称的一个全局例程。因此,我们不应该把 C 函数 简单命名成 abs_eq 之类。通常,在 C 函数名中包含数据类型 名称是个好习惯,这样就不会与其他数据类型的函数发生冲突。
第三,我们本可以把该函数的 PostgreSQL 名称取为 abs_eq,并依靠 PostgreSQL 通过输入数据类型把它与任何其他同名的 PostgreSQL 函数区分开。为了让示例保持简单,这里我们让 C 层和 PostgreSQL 层的函数使用相同的名称。
最后,注意这些操作符函数返回布尔值。实际上,所有被定义为索引访问方法 策略的操作符都必须返回 boolean 类型,因为要与索引 配合使用,它们必须出现在 WHERE 子句的顶层。 (另一方面,支持函数返回的是特定访问方法所期望的类型——在本例中就是一个有符号整数。)
文件中的最后一个例程是我们在讨论 pg_am 表的 amsupport 列时提到的“支持例程”。我们稍后会用到它。现在先忽略它。
现在我们准备好定义操作符了:
CREATE OPERATOR = (
leftarg = complex, rightarg = complex,
procedure = complex_abs_eq,
restrict = eqsel, join = eqjoinsel
);
这里的重要内容是过程名(即上面定义的 C 函数)以及限制和连接选择性函数。 你应当直接使用示例中所用的选择性函数(见 complex.source)。 注意,小于、等于和大于情形有不同的此类函数。必须提供这些函数, 否则优化器将无法有效地使用该索引。
下一步是把这些操作符的项添加到 pg_amop 关系中。为此,我们需要刚才定义的这些操作符的 OID。我们将查找所有取两个 complex 类型操作数的操作符的名称,并挑出我们的那些:
SELECT o.oid AS opoid, o.oprname
INTO TEMP TABLE complex_ops_tmp
FROM pg_operator o, pg_type t
WHERE o.oprleft = t.oid and o.oprright = t.oid
and t.typname = 'complex';
opoid | oprname
--------+---------
277963 | +
277970 | <
277971 | <=
277972 | =
277973 | >=
277974 | >
(6 rows)
(同样,你的某些 OID 号几乎肯定会有所不同。)我们感兴趣的操作符是 OID 从 277970 到 277974 的那些。你得到的值很可能不同,你应当用它们替换下文中的值。我们将用一条 select 语句来完成这件事。
现在我们可以为新操作符类向 pg_amop 中插入项了。这些项必须把我们所需的每个操作符与正确的 B-树策略号关联起来。插入小于操作符的命令如下:
INSERT INTO pg_amop (amopclaid, amopstrategy, amopreqcheck, amopopr)
SELECT opcl.oid, 1, false, c.opoid
FROM pg_opclass opcl, complex_ops_tmp c
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND
opcname = 'complex_abs_ops' AND
c.oprname = '<';
然后对其他操作符照此办理,替换上文第二行中的 1 和最后一行中的 <。注意顺序:“小于”是 1,“小于等于”是 2,“等于”是 3,“大于等于”是 4,“大于”是 5。
字段 amopreqcheck 在此不作讨论;对 B-树操作符它应当总是 false。
最后一步是注册此前在讨论 pg_am 时描述过的“支持例程”。该支持例程的 OID 存储在 pg_amproc 表中,以操作符类 OID 和支持例程编号为键。
首先,我们需要在 PostgreSQL 中注册该函数(回想一下,我们把实现该例程的 C 代码放在了实现操作符例程的那个文件的底部):
CREATE FUNCTION complex_abs_cmp(complex, complex)
RETURNS integer
AS 'PGROOT/src/tutorial/complex'
LANGUAGE C;
SELECT oid, proname FROM pg_proc
WHERE proname = 'complex_abs_cmp';
oid | proname
--------+-----------------
277997 | complex_abs_cmp
(1 row)
(同样,你的 OID 号很可能会有所不同。)
我们可以像下面这样添加新行:
INSERT INTO pg_amproc (amopclaid, amprocnum, amproc)
SELECT opcl.oid, 1, p.oid
FROM pg_opclass opcl, pg_proc p
WHERE
opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') AND
opcname = 'complex_abs_ops' AND
p.proname = 'complex_abs_cmp';
这样就完成了!(呼。)现在应该可以在 complex 列上创建并使用 B-树索引了。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。