I. 教程
II. SQL 语言
III. 服务器管理
- 章14. 安装指导
- 章15. 在 Windows 上安装客户端
- 章16. 操作系统环境
- 章17. 服务器配置
- 章18. 数据库角色和权限
- 章19. 管理数据库
- 章21. 区域
- 章20. 用户认证
- 章22. 日常数据库维护工作
- 章23. 备份与恢复
- 章24. 高可用性与负载均衡
- 章25. 监控数据库的活动
- 章26. 监控磁盘使用情况
- 章27. 可靠性和预写式日志
- 章28. 回归测试
IV. 客户端接口
- Chapter 28. libpq - C 库
- 章29.
- Chapter 30. ecpg - 在 C 里嵌入 SQL
- Chapter 31. 信息模式
- 31.2. 数据类型
- 31.3. informationschemacatalog_name
- 31.4. applicable_roles
- 31.5. check_constraints
- 31.6. columndomainusage
- 31.7. column_privileges
- 31.8. columnudtusage
- 31.9. columns
- 31.10. constraintcolumnusage
- 31.11. constrainttableusage
- 31.12. datatypeprivileges
- 31.13. domain_constraints
- 31.14. domainudtusage
- 31.15. domains
- 31.16. element_types
- 31.17. enabled_roles
- 31.18. keycolumnusage
- 31.20. referential_constraints
- 31.21. rolecolumngrants
- 31.22. roleroutinegrants
- 31.23. roletablegrants
- 31.24. roleusagegrants
- 31.25. routine_privileges
- 31.26. routines
- 31.27. schemata
- 31.28. sql_features
- 31.29. sqlimplementationinfo
- 31.30. sql_languages
- 31.31. sql_packages
- 31.32. sql_sizing
- 31.33. sqlsizingprofiles
- 31.34. table_constraints
- 31.35. table_privileges
- 31.36. tables
- 31.37. triggers
- 31.38. usage_privileges
- 31.39. viewcolumnusage
- 31.40. viewtableusage
- 31.41. views
V. 服务器端编程
- 章33. 扩展 SQL
- 章34. 触发器
- 章35. 规则系统
- 章36. 过程语言
- 章37. PL/pgSQL - SQL 过程语言
- 章38. PL/Tcl - Tcl 过程语言
- 章39. PL/Perl - Perl 过程语言
- 章40. PL/Python - Python 过程语言
- 章41. 服务器编程接口
VI. 参考手册
- I. SQL 命令
- ABORT
- ALTER AGGREGATE
- ALTER CONVERSION
- ALTER DATABASE
- ALTER DOMAIN
- ALTER FUNCTION
- ALTER GROUP
- ALTER INDEX
- ALTER LANGUAGE
- ALTER OPERATOR
- ALTER OPERATOR CLASS
- ALTER ROLE
- ALTER SCHEMA
- ALTER SEQUENCE
- ALTER TABLE
- ALTER TABLESPACE
- ALTER TRIGGER
- ALTER TYPE
- ALTER USER
- ANALYZE
- BEGIN
- CHECKPOINT
- CLOSE
- CLUSTER
- COMMENT
- COMMIT PREPARED
- COMMIT
- COPY
- CREATE AGGREGATE
- CREATE CAST
- CREATE CONSTRAINT TRIGGER
- CREATE CONVERSION
- CREATE DATABASE
- CREATE DOMAIN
- CREATE FUNCTION
- CREATE GROUP
- CREATE INDEX
- CREATE LANGUAGE
- CREATE OPERATOR CLASS
- CREATE OPERATOR
- CREATE ROLE
- CREATE RULE
- CREATE SCHEMA
- CREATE SEQUENCE
- CREATE TABLE
- CREATE TABLE AS
- CREATE TABLESPACE
- CREATE TRIGGER
- CREATE TYPE
- CREATE USER
- CREATE VIEW
- DEALLOCATE
- DECLARE
- DELETE
- DROP OWNED
- DROP AGGREGATE
- DROP CAST
- DROP CONVERSION
- DROP DATABASE
- DROP DOMAIN
- DROP FUNCTION
- DROP GROUP
- DROP INDEX
- DROP LANGUAGE
- DROP OPERATOR CLASS
- DROP OPERATOR
- DROP ROLE
- DROP RULE
- DROP SCHEMA
- DROP SEQUENCE
- DROP TABLE
- DROP TABLESPACE
- DROP TRIGGER
- DROP TYPE
- DROP USER
- DROP VIEW
- END
- EXECUTE
- EXPLAIN
- FETCH
- GRANT
- INSERT
- LISTEN
- LOAD
- LOCK
- MOVE
- NOTIFY
- PREPARE TRANSACTION
- PREPARE
- REASSIGN OWNED
- REINDEX
- RELEASE SAVEPOINT
- RESET
- REVOKE
- ROLLBACK PREPARED
- ROLLBACK TO SAVEPOINT
- ROLLBACK
- SAVEPOINT
- SELECT
- SELECT INTO
- SET
- SET CONSTRAINTS
- SET ROLE
- SET SESSION AUTHORIZATION
- SET TRANSACTION
- SHOW
- START TRANSACTION
- TRUNCATE
- UNLISTEN
- UPDATE
- VACUUM
- II. PostgreSQL 客户端应用程序
- III. PostgreSQL 服务器应用程序
VII. 内部
- 章42. PostgreSQL 内部概貌
- 章43. 系统表
- 43.1. 概述
- 43.2. pg_aggregate
- 43.3. pg_am
- 43.4. pg_amop
- 43.5. pg_amproc
- 43.6. pg_attrdef
- 43.7. pg_attribute
- 43.8. pg_authid
- 43.9. pgauthmembers
- 43.10. pg_autovacuum
- 43.11. pg_cast
- 43.12. pg_class
- 43.13. pg_constraint
- 43.14. pg_conversion
- 43.15. pg_database
- 43.16. pg_depend
- 43.17. pg_description
- 43.18. pg_index
- 43.19. pg_inherits
- 43.20. pg_language
- 43.21. pg_largeobject
- 43.22. pg_listener
- 43.23. pg_namespace
- 43.24. pg_opclass
- 43.25. pg_operator
- 43.26. pg_pltemplate
- 43.27. pg_proc
- 43.28. pg_rewrite
- 43.29. pg_shdepend
- 43.30. pg_shdescription
- 43.31. pg_statistic
- 43.32. pg_tablespace
- 43.33. pg_trigger
- 43.34. pg_type
- 43.35. 系统视图
- 43.36. pg_cursors
- 43.37. pg_group
- 43.38. pg_indexes
- 43.39. pg_locks
- 43.40. pgpreparedstatements
- 43.41. pgpreparedxacts
- 43.42. pg_roles
- 43.43. pg_rules
- 43.44. pg_settings
- 43.45. pg_shadow
- 43.46. pg_stats
- 43.47. pg_tables
- 43.48. pgtimezoneabbrevs
- 43.49. pgtimezonenames
- 43.50. pg_user
- 43.51. pg_views
- 章44. 前/后端协议
- 章45. PostgreSQL 编码约定
- 章46. 本地语言支持
- 章47. 书写一个过程语言处理器
- 章48. 基因查询优化器
- 章49. 索引访问方法接口定义
- 章50. GiST 索引
- 章51. GIN 索引
- 章52. 数据库物理存储
- 章53. BKI 后端接口
- 章54. 规划器如何使用统计信息
VIII. 附录
33.14. 扩展索引接口
到目前为止描述的过程可以让你定义一个新类型、新函数、新操作符。但是,还不能在一个新数据类型的字段上面定义一个索引。为了达到这个目的,必须为新数据类型定义一个操作符类。下面将使用一个真实的例子来描述操作符类:一个用于 B-tree 访问方法的新操作符类,它保存复数并按照绝对值递增的顺序排序。
【注意】在 PostgreSQL 7.3 之前,必须手工给系统表
pg_amop
,pg_amproc
,pg_opclass
添加记录,以便于创建用户定义的操作符类。现在这个方法已经废弃了,因为有了 CREATE OPERATOR CLASS ,它在创建必要的表记录时更简单并且更不容易出错。
33.14.1. 索引方法和操作符类
pg_am
表为每个索引方法(内部称作访问方法)都包含一条记录。对表的普通访问方法支持内建于 PostgreSQL ,但所有访问方法在 pg_am
里都有描述。可以通过定义要求的接口过程并在 pg_am
里创建一个新行的办法增加一个索引访问方法,不过这些些远远超出了本章的内容(参阅章49)。
一个索引方法的过程并不直接知道任何该索引方法将要操作的数据类型的信息。而是操作符类表明索引方法在操作特定数据类型的时候需要使用的操作集合。操作符类的名称的由来是因为它们声明是一种索引可以使用的 WHERE
子句的操作符集(也就是可以转化成一个索引扫描条件)。一个操作符类也可以声明一些索引方法需要的内部操作的支持过程,但是它们并不直接和可以与索引一起使用的 WHERE
子句操作符相关。
可以为同一个数据类型和索引方法定义多个操作符类。这么做的结果是,可以为一种数据类型定义多套索引语义。比如,一个 B-tree 索引要求为它操作的每种数据类型定义一个排序顺序。对于一个复数数据类型而言,有一个通过复数绝对值对数据排序的 B-tree 操作符类可能会有用,还有一个是用实部排序,等等。通常其中一个操作符类会被认为最常用的,并且被标记为该数据类型和索引方法的缺省操作符类。
同样的操作符类名字可以用于多种不同的索引方法(比如 B-tree 和 Hash 访问方法都有叫 int4_ops
的操作符类),但是每个这样的表都是一个独立的实体,必须分别定义。
33.14.2. 索引方法策略
和一种操作符类相关联的操作符是通过"策略号"标识的,策略号用于标识每种操作符在它的操作符类环境里的语义。比如,B-tree 对键字有严格的排序要求,小于到大于,因此,像"小于"和"大于或等于"这样的操作符都是 B-tree 所感兴趣的。因为 PostgreSQL 允许用户定义操作符,PostgreSQL 无法仅通过查看操作符的名字(比如 <
或 >=
)就明白它进行的比较是什么。实际上,索引方法定义了一套"策略",它可以看作一般性的操作符。每种操作符类显示对于特定数据类型而言,是哪种实际操作符对应每种策略以及解释索引的语义。
B-tree 索引定义了五种策略。在表33-2中显示。
表33-2. B-tree 策略
操作 | 策略号 |
---|---|
小于 | 1 |
小于或等于 | 2 |
等于 | 3 |
大于或等于 | 4 |
大于 | 5 |
Hash 索引只表示按位的相等,因此它们只定义了一个策略,在表33-3里显示。
表33-3. Hash 策略
操作 | 策略号 |
---|---|
等于 | 1 |
GiST 索引甚至更加灵活:它们根本就没有固定的策略集。实际上,是每个特定 GiST 操作符类的"一致性"支持过程解释策略号是什么样子。作为示例,有几个内置的 GiST 索引操作符类索引二维几何对象,提供表33-4中所示的"R-tree"策略。其中的四个是两维测试(重叠、相同、包含、包含于)、四个只考虑 x 坐标、四个对 y 坐标进行同样测试。
表33-4. GiST 两维"R-tree"策略
操作 | 策略号 |
---|---|
严格地在...左边 | 1 |
不扩展到...右边 | 2 |
重叠 | 3 |
不延伸到...左边 | 4 |
严格地在...右边 | 5 |
相同 | 6 |
包含 | 7 |
包含于 | 8 |
不扩展到...上面 | 9 |
严格地在...下面 | 10 |
严格地在...上面 | 11 |
不扩展到...下面 | 12 |
GIN 索引在灵活性方面与 GiST 索引类似:它们都没有一个固定的策略集,而是由每个操作符类的支持过程根据操作符类的定义来解释策略号。作为示例,表33-5显示了内置的数组操作符类使用的策略号。
表33-5. GIN 数组策略
操作 | 策略号 |
---|---|
重叠 | 1 |
包含 | 2 |
包含于 | 3 |
相等 | 4 |
请注意,所有策略操作符都返回布尔值。实际上,所有定义为索引方法策略的操作符都必须返回 boolean
类型,因为它们必须出现在一个 WHERE
子句的顶层,这样才能被一个索引使用。
顺便提一下,pg_am
里的 amorderstrategy
字段告诉该索引方法是否支持排序的扫描。零意味着它不支持;如果它支持,那么 amorderstrategy
就是对应该排序操作符的策略号。比如,B-tree 的 amorderstrategy
= 1 是它的"小于"的策略号。
33.14.3. 索引方法支持过程
有时候,策略的信息还不足以让系统决定如何使用某个索引。因此,索引方法需要附加的一些过程来保证正常工作。例如,B-tree 索引方法必须能够比较两个键字以决定其中一个是大于、等于、还是小于另外一个。类似的还有 Hash 索引方法必须能够在键值上计算散列值。这些操作和 SQL 命令条件里使用的操作符并不对应;它们在内部被索引方法的管理过程调用。
就像策略一样,操作符类声明在一定的数据类型和语义解释的条件下,哪个特定函数对应这些角色中的哪一个。索引方法声明它需要的函数集,而操作符类通过给它们赋予"支持函数编号"来标识要正确使用的函数。
B-tree 需要一个表33-6里显示的支持函数。
表33-6. B-tree 支持函数
函数 | 支持号 |
---|---|
比较两个键字并且返回一个小于、等于、大于零的整数,标识第一个键字小于、等于、大于第二个键字。 | 1 |
类似的是 Hash 索引也需要一个支持函数,在表33-7里显示。
表33-7. Hash 支持函数
函数 | 支持号 |
---|---|
为一个键字计算散列值 | 1 |
GiST 索引需要七种支持函数,在表33-8里显示。
表33-8. GiST 支持函数
函数 | 支持号 |
---|---|
一致性 - 检测键是否满足查询限定符 | 1 |
联合 - 计算一套键的联合 | 2 |
压缩 - 计算已索引键或值的压缩结果 | 3 |
解压 - 计算已压缩键或值的解压结果 | 4 |
性能恶化 - 计算使用给定的子树的键向子树中插入新键的性能恶化(penalty) | 5 |
拆分 - 检测页面中的那个项将被移动到新页面并为结果页计算联合键 | 6 |
等于 - 比较两个键并在相等时返回真 | 7 |
GIN 索引需要四种支持函数,在表33-9里显示。
表33-9. GIN 支持函数
函数 | 支持号 |
---|---|
比较 - 比较两个键并返回一个小于、等于、大于零的整数,标识第一个键小于、等于、大于第二个键。 | 1 |
从值中抽取 - 从将被索引的值中抽取键 | 2 |
从查询中抽取 - 从查询条件中抽取键 | 3 |
一致性 - 检测值是否匹配查询条件 | 4 |
和策略操作符不同,支持函数返回特定索引方法预期的数据类型,比如在 B-tree 的情况下,返回一个有符号整数。
33.14.4. 例子
既然已经了解了这些概念,那么现在就来看一个创建新操作符类的例子。你可以在 src/tutorial/complex.c
和 src/tutorial/complex.sql
中找到这里讲述的例子。操作符类封装了那些以绝对值顺序对复数排序的操作符,这样就可以选择 complex_abs_ops
这个名字。首先,需要一个操作符集合。用于定义操作符的过程已经在节33.12讨论过了。对这个用于 B-tree 的操作符类,需要的操作符是:
- 绝对值 小于 (策略 1)
- 绝对值 小于等于 (策略 2)
- 绝对值 等于 (策略 3)
- 绝对值 大于等于 (策略 4)
- 绝对值 大于 (策略 5)
定义一组相关的比较操作符最不容易出错的方法是首先写出 B-tree 比较支持函数,然后再写出其它包装了支持函数的单行函数。这就减少了某些情况下导致不一致结果的机会。根据这个指引,首先写出:
#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y) static int complex_abs_cmp_internal(Complex *a, Complex *b) { double amag = Mag(a), bmag = Mag(b); if (amag < bmag) return -1; if (amag > bmag) return 1; return 0; }
现在,小于函数看起来像这样:
PG_FUNCTION_INFO_V1(complex_abs_lt); Datum complex_abs_lt(PG_FUNCTION_ARGS) { Complex *a = (Complex *) PG_GETARG_POINTER(0); Complex *b = (Complex *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0); }
其它四个函数的不同之处仅在它们如何将内部函数的结果与零比较。
下一步,基于 SQL 函数声明函数和操作符:
CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool
AS 'filename
', 'complex_abs_lt'
LANGUAGE C IMMUTABLE STRICT;
CREATE OPERATOR < (
leftarg = complex, rightarg = complex, procedure = complex_abs_lt,
commutator = > , negator = >= ,
restrict = scalarltsel, join = scalarltjoinsel
);
指定正确的交换器和"非"操作符以及声明限制和连接选择性函数都是非常重要的,否则优化器将无法有效地利用索引。请注意,小于、等于、大于三种情况下应该使用不同的选择性函数。
其它几个值得注意的问题:
只有可以有一个已命名操作符(
=
)把complex
类型做为其两个操作数。这种情况下没有其它用于complex
的=
操作符,但是如果要制作一个实用的数据类型,可能需要=
做为用于复数的普通等于操作的操作符。这种情况下,可能需要使用一些其它操作符名称来命名complex_abs_eq
。尽管 PostgreSQL 可以处理 SQL 名字相同的函数,只要它们的输入数据类型不同,而 C 只能处理一个具有给定名称的全局过程。因此不能把 C 函数命名为像
abs_eq
这样简单的名字。通常在 C 函数名里面包含数据类型名称是一个好习惯,这样就不会和用于其它数据类型的函数冲突。可以制作名为
abs_eq
的 SQL 函数,依靠 PostgreSQL 通过输入数据类型的不同来区分任何其它同名 SQL 函数。为了令例子简单,做的函数在 C 层次和 SQL 层次都有相同的名称。
下一步是注册 B-tree 需要的比较"支持过程"。实现这个例子的 C 代码在包含操作符过程的同一个文件中,下面是定义函数的方法:
CREATE FUNCTION complex_abs_cmp(complex, complex)
RETURNS integer
AS 'filename
'
LANGUAGE C IMMUTABLE STRICT;
既然已经有了需要的操作符和支持过程,就可以最后创建这个操作符类了:
CREATE OPERATOR CLASS complex_abs_ops DEFAULT FOR TYPE complex USING btree AS OPERATOR 1 < , OPERATOR 2 <= , OPERATOR 3 = , OPERATOR 4 >= , OPERATOR 5 > , FUNCTION 1 complex_abs_cmp(complex, complex);
这样就完成了!现在可以在一个 complex
列上创建和使用 B-tree 索引了。
可以把操作符记录写得更冗余一些,像
OPERATOR 1 < (complex, complex) ,
但是如果该操作符接受的数据类型是定义的操作符类处理的东西,那就没必要这么做。
上面的例子假设你想把这个新操作符类作为 complex
数据类型的缺省 B-tree 操作符类。如果你不想这么做,只要去掉关键字 DEFAULT
即可。
33.14.5. 交叉数据类型的操作符类
到目前为止我们都隐含的假定一个操作符类只能处理一种数据类型。虽然每个索引字段都只能是单独一种数据类型,但是使用索引操作符来比较一个已索引字段和一个不同类型的值常常很有用处。目前,B-tree 和 GiST 索引方法支持这样做。
B-trees 要求每个操作符的左操作数必须是已索引数据类型,但右操作数可以是不同的类型。同时必须存在一个匹配的支持函数。例如,内置的 bigint
(int8
) 类型的操作符类允许与 int4
和 int2
进行交叉比较。其定义可以这样写:
CREATE OPERATOR CLASS int8_ops DEFAULT FOR TYPE int8 USING btree AS -- 标准 int8 比较 OPERATOR 1 < , OPERATOR 2 <= , OPERATOR 3 = , OPERATOR 4 >= , OPERATOR 5 > , FUNCTION 1 btint8cmp(int8, int8) , -- 与 int2 (smallint) 交叉比较 OPERATOR 1 < (int8, int2) , OPERATOR 2 <= (int8, int2) , OPERATOR 3 = (int8, int2) , OPERATOR 4 >= (int8, int2) , OPERATOR 5 > (int8, int2) , FUNCTION 1 btint82cmp(int8, int2) , -- 与 int4 (integer) 交叉比较 OPERATOR 1 < (int8, int4) , OPERATOR 2 <= (int8, int4) , OPERATOR 3 = (int8, int4) , OPERATOR 4 >= (int8, int4) , OPERATOR 5 > (int8, int4) , FUNCTION 1 btint84cmp(int8, int4) ;
需要注意的是,这里的定义"重载"了操作符策略和支持函数号。这仅对 B-tree 操作符类是允许的,只要每个数字的实例都有不同的右操作数类型。非交叉类型实例将是该操作符类的默认或主操作符。
GiST 索引不允许重载策略或支持函数号,但通过为每个需要被支持的操作符明确指定策略号,仍然能够从支持多种右操作数数据类型中获益。consistent
支持函数必须基于策略号确定具体需要做什么,并必须准备接受恰当数据类型的比较值。
33.14.6. 操作符类的系统相关性
除了是否可以用于索引外,PostgreSQL 还有多种途径使用操作符类来推断操作符性质。因此,即使并不打算为你自定义的数据类型索引在任何字段上建立索引,你可能还是希望创建操作符类。
特别是诸如 ORDER BY
和 DISTINCT
之类需要对值进行比较和排序的 SQL 特性。要在自定义的数据类型上实现这些特性,PostgreSQL 将会为该类型查找默认的 B-tree 操作符类。该操作符类中的"equals"成员为 GROUP BY
和 DISTINCT
定义了相等的概念,同时操作符类的排序顺序定义了默认的 ORDER BY
排序。
用户自定义类型数组的比较同样页依赖于默认 B-tree 操作符类的语意。
如果对于某个数据类型不存在默认 B-tree 操作符类,那么系统将会自动寻找默认的 Hash 操作符类。但因为 Hash 操作符类仅仅提供相等比较,所以在实践中它仅能用于数组的相等性测试。
如果某个数据类型不存在任何缺省操作符类,你就会在使用该 SQL 特性时得到一个类似"could not identify an ordering operator"的错误。
【注意】PostgreSQL 7.4 以前,排序和分组操作隐含使用名为
=
,<
,>
的操作符。新的依赖默认操作符类的行为避免了对任何特定操作符名的行为的假定。
33.14.7. 操作符类的特殊特性
还有两种操作符类的特殊特性没有讨论,主要是因为它们对于缺省的 B-tree 索引方法并不非常有用。
通常,把一个操作符声明为一个操作符类的成员意味着索引方法可以使用该操作符检索满足 WHERE
条件的行集合。比如
SELECT * FROM table WHERE integer_column < 4;
可以由一个建立在整数字段上的 B-tree 索引精确地满足。但是有时候会有这样的现像:索引是用作匹配数据行的并不精确的指向。比如,如果一个 GiST 索引只为对象存储周界的方块,那么它就无法精确地满足两个非方形对象(比如多边形)之间是否覆盖的 WHERE
条件测试。但是可以使用这个索引找出那些周界方块和目标对象的周界方块重合的对象,然后只在索引找到的对象上做精确的重合测试。如果这种情形可以通过,那就说索引对操作符是"松散的",并且在 CREATE OPERATOR CLASS
命令里给 OPERATOR
子句增加 RECHECK
。如果索引保证返回所有要求的行加上一些附加的行,那么 RECHECK
就合法,这些额外的行就可以通过执行最初的操作符调用消除。
再考虑只在索引中存储复杂对象(比如多边形)的周界方块的情形。这种情况下在索引条目里存储整个多边形没有太多的数值(也可以只存储更简单的 box
类型对象)。这种情形由 CREATE OPERATOR CLASS
里的 STORAGE
选项存储。可以写类似这样的东西:
CREATE OPERATOR CLASS polygon_ops DEFAULT FOR TYPE polygon USING gist AS ... STORAGE box;
目前,只有 GiST 和 GIN 索引方法支持与字段数据类型不同的 STORAGE
类型。GiST compress
和 decompress
支持过程在使用 STORAGE
的时候必须处理数据类型转换。对于 GIN 来说,STORAGE
类型标识了"键"值的类型,它通常与索引字段的类型不同。比如,一个用于整数数组字段的操作符类可能正好有整数类型的键。GIN extractValue
和 extractQuery
支持过程负责从已索引的值抽取键字。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论