您的位置:寻梦网首页编程乐园数据库PostgreSQL 7.2 Documentation

17.6. 创建操作符和支持过程

因此,现在我们有了一个访问方式和一个操作符表. 我们还需要一套操作符; 用于定义操作符的过程已经在 Chapter 14 讨论过了. 对这个用于 B-tree 的 complex_abs_ops 操作符表, 我们需要的操作符是:

  • absolute-value less-than (strategy 1)
  • absolute-value less-than-or-equal (strategy 2)
  • absolute-value equal (strategy 3)
  • absolute-value greater-than-or-equal (strategy 4)
  • absolute-value greater-than (strategy 5)

假设实现这些函数的代码放在文件 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 子句的顶层才能和一个索引一起使用. (另一方面,支持函数返回特定的访问模式的希望值 -- 在这种情况下是一个符号整数.)

文件里最终的过程是我们在讨论表 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-tree 策略数与我们使用的每个操作符相关联.插入小于操作符的命令象下面这个样子∶

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-tree 操作符,它应该总是假.

最后一步是注册我们前面讨论 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-tree 索引了.