第 6 章 类型转换
目录
SQL 查询可能有意或无意地要求在同一表达式中混用不同的数据类型。Postgres 提供了丰富的机制来求值混合类型表达式。
在很多情况下,用户不需要了解类型转换机制的细节。但 Postgres 所做的隐式转换会影响查询的表面结果,用户或程序员可以用显式类型强制转换来定制这些结果。
本章介绍 Postgres 的类型转换机制和约定。关于特定数据类型及允许的函数和操作符的更多信息,请参阅用户指南和程序员指南中的相关章节。
程序员指南中有关于隐式类型转换和强制转换所用确切算法的更多细节。
6.1. 概述
SQL 是一种强类型语言。也就是说,每个数据项都有一个相关联的数据类型,它决定了该数据项的行为和允许的用法。Postgres 拥有一个可扩展的类型系统,比其他 RDBMS 实现更加通用和灵活。因此,Postgres 中大部分类型转换行为应当由一般规则而不是 ad-hoc(特定)启发式规则支配,以使混合类型表达式即使在有用户定义类型时也有意义。
Postgres 的扫描器/解析器只把词法元素解码成五种基本类别:整数、浮点数、字符串、名字和关键字。大多数扩展类型首先被词法化为字符串。SQL 语言定义允许用字符串指定类型名,这一机制可以用来让解析器走上正确的路径。例如,查询
tgl=> SELECT text 'Origin' AS "Label", point '(0,0)' AS "Value"; Label |Value ------+----- Origin|(0,0) (1 row)
中有两个字符串,类型分别为 text 和 point。如果没有指定类型,则最初会赋予占位类型 unknown,并在后面阶段按如下所述解析。
在 Postgres 解析器中有四种基本的 SQL 结构需要专门的类型转换规则:
- 操作符
Postgres 允许带左一元和右一元(单参数)操作符的表达式,也允许二元(双参数)操作符的表达式。
- 函数调用
Postgres 类型系统的很大一部分是围绕一套丰富的函数构建的。函数调用有一个或多个参数,对任何具体的查询,这些参数都必须与系统目录中可用的函数匹配。
- 查询目标
SQL 的 INSERT 语句把查询的结果放到表中。查询中的表达式必须与插入的目标列匹配,必要时还要转换成目标列的类型。
- UNION 查询
由于 UNION SELECT 语句的所有查询结果必须出现在一组列中,每个 SELECT 子句的类型必须匹配并转换成统一的集合。
许多一般类型转换规则使用了建立在
Postgres 函数和操作符系统表之上的简单约定。转换规则中还包含一些启发式规则,以便更好地支持
SQL92 标准固有类型(如
smallint、integer 和 float)的约定。
Postgres 解析器使用的约定是:所有类型转换函数都接受一个源类型的参数,并且以目标类型的名称命名。满足这一标准的任何函数都被认为是有效的转换函数,解析器可以把它当作转换函数使用。这个简单的假设使解析器无需硬编码就能探索类型转换的可能性,让扩展的用户定义类型透明地使用这些相同的特性。
解析器中还提供了一个额外的启发式规则,以便对 SQL 标准类型的正确行为做出更好的猜测。定义了五种类型类别:boolean、string、numeric、geometric 和用户自定义。除用户自定义外,每个类别都有一个用于解决候选歧义的"首选类型"。每个"用户自定义"类型都是自己的"首选类型",因此只含一个用户定义类型的有歧义表达式(有多个候选解析解的表达式)可以解析出单一的最佳选择,而含多个用户定义类型的表达式仍会有歧义并报错。
候选解只落在一个类型类别内的有歧义表达式很可能得到解析,而候选跨越多个类别的有歧义表达式则很可能报错并请求用户澄清。
6.1.1. 指南
所有类型转换规则的设计都考虑了以下几条原则:
隐式转换绝不应有令人意外或不可预测的结果。
解析器没有先验知识的用户定义类型在类型层级中应该更"高"。在混合类型表达式中,固有类型总是应当转换成用户定义类型(当然,只在需要转换时如此)。
用户定义的类型之间没有关联。目前,Postgres 没有关于类型之间关系的信息,只有内置类型的硬编码启发式规则以及基于目录中可用函数的隐式关系。
如果查询不需要隐式类型转换,解析器或执行器就不应有额外的开销。也就是说,如果查询构造良好且类型已经匹配,查询就应当继续进行,不必在解析器上花费额外时间,也不必向查询中引入不必要的隐式转换函数。
此外,如果某个查询通常需要为函数做隐式转换,而之后用户用正确的参数类型定义了一个显式函数,解析器就应使用这个新函数,不再用旧函数做隐式转换。