既然我们已经了解了这些概念,那么现在就是我们承诺过的创建一个 新的操作符表的例子。首先,我们需要一个操作符集合。 用于定义操作符的过程已经在Chapter 11讨论过了. 对这个用于 B-tree 的 complex_abs_ops 操作符表, 我们需要的操作符是:
假设实现这些函数的代码放在文件 PGROOT/tutorial/complex.c里, 我们已经把它编译成 PGROOT/tutorial/complex.so.
有一部分 C 代码看起来象∶
#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 函数名里面包含数据类型名称是一个好习惯, 这样就不会和用于其它数据类型的函数冲突.
第三,我们可以制作函数abs_eq的 PostgreSQL名称, 依靠 PostgreSQL 通过输入数据类型的不同区分任何其他同名 PostgreSQL函数. 为了令例子简单,我们做的函数在 C 层次和 PostgreSQL层次都有相同的名称.
最后,请注意这些操作符函数返回布尔值. 实际上,所有定义为索引访问方法策略的操作符都必须返回boolean, 因为它们必须出现在WHERE子句的顶层才能和一个索引一起使用. (另一方面,支持函数返回特定的访问方法的希望值 -- 对于 B-tree 的比较函数而言,是一个符号整数.)
现在可以定义使用它们的操作符了:
CREATE OPERATOR = ( leftarg = complex, rightarg = complex, procedure = complex_abs_eq, restrict = eqsel, join = eqjoinsel );
里的重要问题是过程名称(就是上面定义的 C 函数) 和这个关系和连接选择性函数. 你应该只使用例子里(参阅 complex.source )的选择性函数.请注意还要有这样的用于小于,等于和大于情况的函数. 你必须提供这些(函数),否则优化器将无法有效地使用该索引.
下一步是注册 B-tree 需要的比较"支持过程"。 实现这个的 C 代码在包含操作符过程的同一个文件中:
CREATE FUNCTION complex_abs_cmp(complex, complex) RETURNS integer AS 'PGROOT/src/tutorial/complex' LANGUAGE C;