36.2. PostgreSQL 类型系统 #
PostgreSQL 数据类型可以分为基础类型、容器类型、域和伪类型。
36.2.1. 基础类型 #
基础类型是像 integer 这样的类型,它们在
SQL 语言层之下实现(通常使用像 C 这样的底层语言)。它们通常对应于通常所说的抽象数据类型。PostgreSQL 只能通过用户提供的函数来操作这类类型,并且只能在用户所描述的范围内理解这类类型的行为。内置基础类型见第 8 章。
枚举(enum)类型可以看作是基础类型的一个子类。其主要区别在于,它们只需使用 SQL 命令即可创建,而不需要任何底层编程。更多信息参见第 8.7 节。
36.2.2. 容器类型 #
PostgreSQL 有三种“容器”类型,即包含其他类型多个值的类型。它们是数组、复合类型和范围类型。
数组可以保存多个同一类型的值。每一种基础类型、复合类型、范围类型和域类型都会自动创建一个对应的数组类型。但不存在数组的数组。就类型系统而言,多维数组与一维数组并无区别。更多信息参见第 8.15 节。
复合类型,也称行类型,会在用户创建表时自动产生。也可以使用 CREATE TYPE 定义一个没有关联表的“独立”复合类型。复合类型只是带有关联字段名的一组类型。复合类型的值是一行或一条记录,其中包含各字段的值。更多信息参见第 8.16 节。
范围类型可以保存同一类型的两个值,它们分别是该范围的下界和上界。范围类型由用户创建,不过也存在少数内置的范围类型。更多信息参见第 8.17 节。
36.2.3. 域 #
域基于某一种特定的底层类型,并且在很多场合下都可以与其底层类型互换。不过,域可以带有约束,把其合法值限制在底层类型所允许值的一个子集内。域使用 SQL 命令 CREATE DOMAIN 创建。更多信息参见第 8.18 节。
36.2.4. 伪类型 #
有少数几种“伪类型”用于特殊目的。伪类型不能作为表的列或容器类型的组成部分出现,但它们可以用来声明函数的参数类型和结果类型。这为在类型系统内部识别特殊类别的函数提供了一种机制。表 8.27 列出了现有的伪类型。
36.2.5. 多态类型 #
特别值得关注的一些伪类型是多态类型,它们用于声明多态函数。这一强大特性允许单个函数定义作用于许多不同的数据类型,而具体的数据类型则由某次调用中实际传给它的数据类型决定。多态类型见表 36.1。它们的一些用法示例见第 36.5.11 节。
表 36.1. 多态类型
| 名称 | 家族 | 说明 |
|---|---|---|
anyelement | 简单 | 表示函数接受任意数据类型 |
anyarray | 简单 | 表示函数接受任意数组数据类型 |
anynonarray | 简单 | 表示函数接受任意非数组数据类型 |
anyenum | 简单 | 表示函数接受任意枚举数据类型(见第 8.7 节) |
anyrange | 简单 | 表示函数接受任意范围数据类型(见第 8.17 节) |
anymultirange | 简单 | 表示函数接受任意多范围数据类型(见第 8.17 节) |
anycompatible | 通用 | 表示函数接受任意数据类型,并自动把多个参数提升到一个公共数据类型 |
anycompatiblearray | 通用 | 表示函数接受任意数组数据类型,并自动把多个参数提升到一个公共数据类型 |
anycompatiblenonarray | 通用 | 表示函数接受任意非数组数据类型,并自动把多个参数提升到一个公共数据类型 |
anycompatiblerange | 通用 | 表示函数接受任意范围数据类型,并自动把多个参数提升到一个公共数据类型 |
anycompatiblemultirange | 通用 | 表示函数接受任意多范围数据类型,并自动把多个参数提升到一个公共数据类型 |
多态参数和结果彼此关联,并且会在解析调用多态函数的查询时解析为具体的数据类型。当存在多个多态参数时,输入值的实际数据类型必须按下文所述彼此匹配。如果函数的结果类型是多态的,或者它带有多态类型的输出参数,则这些结果的类型将根据下文所述的多态输入的实际类型推导出来。
对于“简单”家族的多态类型,匹配和推导规则如下:
每个声明为 anyelement 的位置(参数或返回值)都允许具有任意具体的实际数据类型,但在同一次调用中它们必须全部是相同的实际类型。每个声明为 anyarray 的位置都可以是任意数组数据类型,但同样它们必须全部是同一类型。类似地,声明为
anyrange 的位置必须全部是同一范围类型。anymultirange 也一样。
此外,如果某些位置声明为 anyarray,另一些位置声明为
anyelement,那么 anyarray 位置上的实际数组类型必须是一个数组,其元素类型与 anyelement 位置上出现的类型相同。anynonarray 的处理方式与
anyelement 完全相同,只是额外增加了实际类型不能是数组类型的约束。anyenum 的处理方式也与
anyelement 完全相同,只是额外增加了实际类型必须是枚举类型的约束。
类似地,如果某些位置声明为 anyrange,另一些位置声明为
anyelement 或 anyarray,那么
anyrange 位置上的实际范围类型必须是一个范围,其子类型与
anyelement 位置上的类型相同,也与
anyarray 位置的元素类型相同。如果有位置声明为
anymultirange,其实际多范围类型必须包含与声明为
anyrange 的参数相匹配的范围,并且其基础元素与声明为
anyelement 和 anyarray 的参数相匹配。
因此,当多个参数位置被声明为多态类型时,其总体效果就是只允许某些实际参数类型组合。例如,声明为
equal(anyelement, anyelement) 的函数可以接受任意两个输入值,只要它们属于同一数据类型。
当函数的返回值声明为多态类型时,必须至少有一个参数位置也属于多态,而为这些多态参数提供的实际数据类型将决定该次调用的实际结果类型。例如,假设还没有数组下标机制,就可以把实现下标的函数定义为
subscript(anyarray, integer) returns anyelement。这一声明约束实际的第一个参数必须是数组类型,并允许解析器从该参数的实际类型推断出正确的结果类型。另一个例子是,声明为
f(anyarray) returns anyenum 的函数将只接受枚举类型的数组。
在大多数情况下,解析器可以从同一家族中其他多态类型的参数推导出多态结果类型的实际数据类型;例如,anyarray 可以从
anyelement 推导出来,反之亦然。一个例外是,anyrange 类型的多态结果需要存在一个
anyrange 类型的参数;它不能从 anyarray 或
anyelement 参数推导出来。这是因为可能存在多个具有相同子类型的范围类型。
注意,anynonarray 和 anyenum 并不表示独立的类型变量;它们与 anyelement 是同一个类型,只是附加了额外约束。例如,把函数声明为
f(anyelement, anyenum),等价于把它声明为
f(anyenum, anyenum):两个实际参数都必须是同一种枚举类型。
对于“通用”家族的多态类型,匹配和推导规则与“简单”家族大致相同,但有一个主要区别:参数的实际类型不必完全一致,只要它们都能隐式转换为某个公共类型即可。这个公共类型的选择遵循与 UNION 及相关构造相同的规则(见第 10.5 节)。选择公共类型时,会考虑
anycompatible 和 anycompatiblenonarray 输入的实际类型、anycompatiblearray 输入的数组元素类型、anycompatiblerange 输入的范围子类型,以及
anycompatiblemultirange 输入的多范围子类型。如果出现了
anycompatiblenonarray,那么公共类型必须是非数组类型。一旦确定了公共类型,位于 anycompatible 和
anycompatiblenonarray 位置的参数会自动转换为该类型,而位于 anycompatiblearray 位置的参数会自动转换为该公共类型对应的数组类型。
由于只知道子类型时无法选择出对应的范围类型,所以使用
anycompatiblerange 和/或
anycompatiblemultirange 时,要求所有声明为该类型的参数都具有相同的实际范围和/或多范围类型,并且该类型的子类型必须与所选公共类型一致,这样就不需要对范围值进行类型转换。和
anyrange 及 anymultirange 一样,把
anycompatiblerange 和 anycompatiblemultirange 用作函数结果类型时,也要求存在一个 anycompatiblerange 或
anycompatiblemultirange 参数。
注意,不存在 anycompatibleenum 类型。这样的类型不会很有用,因为通常不存在到枚举类型的隐式类型转换,这意味着无法为不同的枚举输入解析出公共类型。
“简单”和“通用”这两个多态家族代表两组彼此独立的类型变量。请看下面的例子:
CREATE FUNCTION myfunc(a anyelement, b anyelement,
c anycompatible, d anycompatible)
RETURNS anycompatible AS ...
在实际调用该函数时,前两个输入必须完全是同一类型。后两个输入必须能够提升为某个公共类型,但这个类型不需要与前两个输入的类型有任何关系。结果将采用后两个输入的公共类型。
可变参数函数(即接受可变数量参数的函数,如第 36.5.6 节所述)也可以是多态的:其方式是把最后一个参数声明为
VARIADIC anyarray 或
VARIADIC anycompatiblearray。就参数匹配和确定实际结果类型而言,这样的函数与写出适当数量的
anynonarray 或 anycompatiblenonarray 参数的效果相同。
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.