37.2. PostgreSQL 类型系统 #
PostgreSQL 数据类型可以分为基础类型、容器类型、域和伪类型。
37.2.1. 基础类型
基础类型是像 integer 这样的类型,它们在
SQL 语言层之下实现(通常使用像 C 这样的底层语言)。它们通常对应于通常所说的抽象数据类型。PostgreSQL 只能通过用户提供的函数来操作这类类型,并且只能在用户所描述的范围内理解这类类型的行为。内置基础类型见第 8 章。
枚举(enum)类型可以看作是基础类型的一个子类。其主要区别在于,它们只需使用 SQL 命令即可创建,而不需要任何底层编程。更多信息参见第 8.7 节。
37.2.2. 容器类型
PostgreSQL 有三种“容器”类型,即包含其他类型多个值的类型。它们是数组、复合类型和范围类型。
数组可以保存多个同一类型的值。每一种基础类型、复合类型、范围类型和域类型都会自动创建一个对应的数组类型。但不存在数组的数组。就类型系统而言,多维数组与一维数组并无区别。更多信息参见第 8.15 节。
复合类型,也称行类型,会在用户创建表时自动产生。也可以使用 CREATE TYPE 定义一个没有关联表的“独立”复合类型。复合类型只是带有关联字段名的一组类型。复合类型的值是一行或一条记录,其中包含各字段的值。更多信息参见第 8.16 节。
范围类型可以保存同一类型的两个值,它们分别是该范围的下界和上界。范围类型由用户创建,不过也存在少数内置的范围类型。更多信息参见第 8.17 节。
37.2.3. 域 #
域基于某一种特定的底层类型,并且在很多场合下都可以与其底层类型互换。不过,域可以带有约束,把其合法值限制在底层类型所允许值的一个子集内。域使用 SQL 命令 CREATE DOMAIN 创建。更多信息参见第 8.18 节。
37.2.4. 伪类型
有少数几种“伪类型”用于特殊目的。伪类型不能作为表的列或容器类型的组成部分出现,但它们可以用来声明函数的参数类型和结果类型。这为在类型系统内部识别特殊类别的函数提供了一种机制。表 8.27 列出了现有的伪类型。
37.2.5. 多态类型 #
五种特别值得关注的伪类型是 anyelement、anyarray、anynonarray、anyenum 和 anyrange,它们统称为多态类型。使用这些类型声明的函数称为多态函数。多态函数可以操作多种不同的数据类型,具体类型由某次调用时实际传入的数据类型决定。
多态参数和结果相互关联,在解析调用多态函数的查询时,会被解析为某种具体的数据类型。每个声明为 anyelement 的位置(参数或返回值)都可以具有任意具体的实际数据类型,但在同一次调用中,它们必须具有相同的实际类型。每个声明为 anyarray 的位置可以具有任意数组数据类型,但同样必须全部具有相同的类型。类似地,声明为 anyrange 的位置必须全部具有相同的范围类型。此外,如果某些位置声明为 anyarray,另一些位置声明为 anyelement,那么 anyarray 位置上的实际数组类型,其元素类型必须与 anyelement 位置上的类型相同。类似地,如果某些位置声明为 anyrange,另一些位置声明为 anyelement 或 anyarray,那么 anyrange 位置上的实际范围类型,其子类型必须与 anyelement 位置上的类型相同,并与 anyarray 位置上的元素类型相同。anynonarray 的处理与 anyelement 完全相同,但增加了实际类型不能为数组类型的约束。anyenum 的处理与 anyelement 完全相同,但增加了实际类型必须为枚举类型的约束。
因此,当多个参数位置被声明为多态类型时,其总体效果就是只允许某些实际参数类型组合。例如,声明为
equal(anyelement, anyelement) 的函数可以接受任意两个输入值,只要它们属于同一数据类型。
如果函数的返回值声明为多态类型,那么至少有一个参数位置也必须是多态的,并且实际传入的参数数据类型决定该次调用的实际结果类型。例如,如果尚不存在数组下标机制,可以将实现下标访问的函数定义为 subscript(anyarray, integer) returns anyelement。这一声明将实际的第一个参数约束为数组类型,并允许解析器根据第一个参数的实际类型推导正确的结果类型。另一个例子是,声明为 f(anyarray) returns anyenum 的函数只接受枚举类型的数组。
在大多数情况下,解析器可以根据属于另一种多态类型的参数,推导出多态结果类型的实际数据类型。例如,可以从 anyelement 推导出 anyarray,反之亦然。例外是:类型为 anyrange 的多态结果要求有一个类型为 anyrange 的参数;无法从 anyarray 或 anyelement 参数推导出来。这是因为多个范围类型可能具有相同的子类型。
注意,anynonarray 和 anyenum 并不表示独立的类型变量;它们与 anyelement 是同一个类型,只是附加了额外约束。例如,把函数声明为
f(anyelement, anyenum),等价于把它声明为
f(anyenum, anyenum):两个实际参数都必须是同一种枚举类型。
可变参数函数(接受可变数量参数的函数,见第 37.5.5 节)可以是多态的:将其最后一个参数声明为 VARIADIC anyarray 即可。就参数匹配和确定实际结果类型而言,这样的函数与写出相应数量的 anynonarray 参数时行为相同。