SQL 语句可能(有意无意地)要求在同一表达式里混合不同的数据类型。 PostgreSQL 在计算混合类型表达式方面有许多扩展性很强的功能。
在大多数情况下,用户不需要明白类型转换机制的细节。 但是,由 PostgreSQL 所进行的隐含的类型转换会对查询的结果产生影响, 必要时这些影响又可以用明确的类型转换进行剪裁利用。
本章介绍 PostgreSQL类型转换的传统和机制。 关于特定的类型和函数及操作符的进一步信息, 请参考Chapter 8 和 Chapter 9里的相关章节。
SQL 是强类型语言。 也就是说,每一数据都与一个决定其行为和可行用法的数据类型相联。 PostgreSQL 有一个可扩展的数据类型系统, 该系统比其他 SQL 实现实现更具通用性和灵活性。 因而,PostgreSQL中大多数类型转换的特性是由通用规则来管理的, 而不是由专门搜索方法来分析,以此令混合类型表达式有实际意义,即便是用户定义的类型也如此。
PostgreSQL 扫描/分析器只将词法元素分解成五个基本种类: 整数(integers),浮点数(floating-point numbers), 字符串(strings),名字(names)和关键字(keywords)。 大多数扩展的类型首先表征为字符串(strings)。 SQL 语言的定义允许将类型名声明为字符串,这个机制被 PostgreSQL 用于令分析器沿着正确的方向运行。例如,下面查询
SELECT text 'Origin' AS "label", point '(0,0)' AS "value"; label | value --------+------- Origin | (0,0) (1 row)
有两个文本常量,类型分别为 text 和 point。 如果没有为字串文本声明类型, (该文本)先被初始化成一个拥有存储空间的 unknown(未知)类型, 该类型将在后面描述的晚期阶段分析。
在 PostgreSQL 分析器里, 有四种基本的 SQL 元素需要独立的类型转换规则:
多数 PostgreSQL 类型系统是建筑在一套丰富的函数上的。 函数调用可以有一个或多个参数。 因为 PostgreSQL 允许函数重载, 所以函数名自身并不唯一地标识将要调用的函数 — 分析器必须以函数提供的参数的类型为基础选择正确的函数。
PostgreSQL 也允许使用左目或右目操作符(单目操作符,一个参数), 允许表达式里使用双目操作符(两个参数)。
SQL INSERT和UPDATE 语句将表达式结果放入表中。 语句中的表达式类型必须和的目标列的类型一致或者是(可能需要)转换成一致的。
因为联合SELECT语句中的所有查询结果必须在一列里显示出来,
所以每个SELECT
子句中的元素类型必须相互匹配并转换成一套统一类型。
类似,一个CASE构造的结果表达式必须转换成统一的类型,
这样CASE表达式自身作为整体有一种已知输出类型。
同样的要求也存在于 ARRAY 构造中,
以及 GREATEST
和 LEAST
函数。
系统表存储有关哪种数据类型之间的转换(叫 casts )是合法的, 以及如何执行这些转换的信息。额外的转换可以由用户通过 CREATE CAST 命令增加。(通常和定义一种新的数据类型一起完成。 内置类型的类型转换集已经经过仔细的雕琢了,因此最好不要去更改它们。)
分析器中还有一个搜索器用于更好地猜测 SQL 标准类型的确切特性。 分析器里定义了几种类型范畴: boolean,numeric,string, bitstring,datetime,timespan, geometric,network,和用户定义(user-defined)。 除用户定义类型外, 每种类型都有一种首选类型用于解决类型定义歧义的问题。 对于用户定义的类型,其自身就是自己的首选类型,所以那些含混不清的表达式(在分析结果中有多种可能的表达式) 如果有多个内置类型的时候大多可以正确分析,但如果有多个用户定义类型可选,则会抛出错。
所有类型转换规则都是建立在下面几个基本原则上的:
隐含转换决不能有奇怪的或不可预见的输出。
用户定义类型,因为分析器对其没有预先的认识, 在类型级别中应该级别较"高"。 在混合类型的表达式里,内部类型总是应该转换成用户定义类型。 (当然只是在必须转换的时候)。
用户定义类型是不相关的。目前,PostgreSQL 除了用于内部数据类型的硬代码搜索器和以现有函数为基础的隐含类型关系外,没有任何可用于处理类型间关系的信息。
如果一个查询不需要隐含的类型转换, 分析器或执行器不应该进行更多的额外操作。 这就是说,任何一个类型匹配,格式清晰的查询不应该在分析器里耗费更多的时间, 也不应该向查询中引入任何不必要的隐含类型转换调用。
另外,如果一个查询通常使用某个函数进行隐含类型转换, 而用户定义了一个有正确参数的函数,解释器应该使用新函数取代原先旧函数的隐含操作。