PostgreSQL 13.1 中文入门教程 PostgreSQL PostgreSQL类型系统

2024-02-25 开发教程 PostgreSQL 13.1 中文入门教程 匿名 4
37.2.1. 基础类型
37.2.2. 容器类型
37.2.3. 域
37.2.4. 伪类型
37.2.5. 多态类型

PostgreSQL数据类型被划分为基础类型、容器类型、域和伪类型。

37.2.1. 基础类型

基础类型是那些被实现在SQL语言层面之下的类型(通常用一种底层语言,如 C),例如integer。它们通常对应于常说的抽象数据类型。PostgreSQL只能通过由用户提供的函数在这类类型上操作,并且只能理解到用户描述这种类型行为的程度。第 8 章中描述了内建的基础类型。

枚举(enum)类型可以被认为是基础类型的一个子类。主要区别是它们可以使用SQL命令创建,不需要用到底层的编程。更多信息请参考第 8.7 节。

37.2.2. 容器类型

PostgreSQL有三种“容器”类型,它们是包含多个其他类型值的类型。它们是数组、组合以及范围。

数组可以保存全部是同种类型的多个值。为每一种基本类型、组合类型、范围类型以及域类型都会自动创建一个数组类型。但是没有数组的数组。就类型系统的认知而言,多维数组就和一维数组一样。更多信息请参考第 8.15 节。

只要用户创建一个表,就会创建组合类型或者行类型。也可以使用CREATE TYPE来定义一个没有关联表的“stand-alone”组合类型。一个组合类型只是一个具有相关域名称的类型列表。一个组合类型的值是一个行或者域值记录。用户可以访问来自 SQL查询的组成域。更多信息请参考第 8.16 节。

范围类型可以保存同种类型的两个值,它们是该范围的上下界。范围类型是用户创建的,不过也存在一些内建的范围类型。更多信息请参考第 8.17 节。

37.2.3. 域

一个域是基于一种特定底层类型的,并且出于很多目的它可以与其底层类型互换。不过,一个域能够具有约束来限制它的合法值于其底层基础类型允许值的一个子集。可以使用SQL命令CREATE DOMAIN创建域。更多信息请参考 第 8.18 节。

37.2.4. 伪类型

有一些用于特殊目的“伪类型”。伪类型不能作为表列或者容器类型的组件出现,但是它们能被用于声明函数的参数和结果类型。这在类型系统中提供了一种机制来标识函数的特殊分类。表 8.27列出了现有的伪类型。

37.2.5. 多态类型

特别让人感兴趣的一些伪类型是polymorphic types,用于声明polymorphic functions。 这种强大的特性允许单个函数定义对许多不同的数据类型进行操作,具体数据类型由特定调用中实际传递给它的数据类型确定。 多态类型显示在表 37.1中。 它们的一些用例出现在 第 37.5.10 节里面。

表 37.1. Polymorphic Types

名称家族描述

anyelement

Simple表示函数接受任何数据类型

anyarray

Simple表示函数接受任何数组数据类型

anynonarray

Simple表示函数接受任何非数组数据类型

anyenum

Simple表示函数接受任何enum数据类型(参见 第 8.7 节)

anyrange

Simple表示函数接受任何范围数据类型(参见 第 8.17 节)

anycompatible

Common表示函数接受任何数据类型,并自动将多个参数提升为公共数据类型

anycompatiblearray

Common表示函数接受任何数组数据类型,并自动将多个参数提升为公共数据类型

anycompatiblenonarray

Common表示函数接受任何非数组数据类型,并自动将多个参数提升为公共数据类型

anycompatiblerange

Common表示函数接受任何范围数据类型,并自动将多个参数提升为公共数据类型

多态参数和结果是相互关联的,并且它们在解析调用多态函数的查询时被决定到一种特定的数据类型。 当存在多个多态参数时,输入值的实际数据类型必须匹配如下所述。 如果函数的结果类型是多态的,或者它具有多态类型的输出参数,则这些结果的类型是根据下面所描述的多态输入的实际类型推导出来的。

对于多态类型的 “simple”家族, 匹配和推导规则是这样工作的:

每一个被声明为anyelement的位置(参数或返回值)被允许具有任意特定的实际数据类型,但是在任何给定的查询中它们必须全部是相同的实际类型。 每一个被声明为anyarray的位置可以有任意数组数据类型,但是相似地,它们必须全部具有相同类型。 并且类似地,被声明为anyrange的位置必须是全部是相同的范围类型。 此外,如果有位置被声明为anyarray并且其他位置被声明为anyelementanyarray位置中的实际数组类型必须是一个数组,该数组的元素都是出现在anyelement位置的同一种类型。 相似地,如果有位置被声明为anyrange并且其他位置被声明为 anyelementanyarrayanyrange位置的实际范围类型必须是一个范围,该范围的子类型是出现在anyelement位置的同一种类型,并且与 anyarray位置的元素类型相同。 anynonarray被当做和 anyelement相同,但是增加了额外的约束要求实际类型不能是一种数组类型。 anyenum被当做和anyelement相同,但是增加了额外的约束要求实际类型不能是一种枚举类型。

因此,当使用一种多态类型声明了多于一个参数位置,有效效果是只有实际参数类型的某些组合才被允许。例如,一个被声明为equal(anyelement, anyelement)的函数将要求任意两个输入值,只要它们是同一种数据类型。

当一个函数的返回值被声明为多态类型时,必须至少有一个参数位置也是多态的,并且为多态参数提供的实际数据类型确定该调用的实际结果类型。 例如,如果还没有一种数组下标机制,我们可以定义一个函数来实现下标:subscript(anyarray, integer) returns anyelement。这个声明约束了实际的第一个参数是一种数组类型,并且允许解析器从实际的第一个参数类型推断正确的结果类型。 另一个例子是一个被声明为f(anyarray) returns anyenum的函数将只接受枚举类型的数组。

在大多数情况下,解析器可以从同一家族中不同多态类型的参数推断出多态结果类型的实际数据类型; 例如,anyarray可以从anyelement推断出来,反之亦然。 一个例外是,类型anyrange的多态结果需要类型anyrange的参数;它不能从anyarrayanyelement参数中推断出来。 这是因为可能有多个范围类型具有相同的子类型。

注意anynonarrayanyenum并不表示独立的类型变量,它们是和anyelement相同的类型,只是有一个额外的约束。例如,将一个函数声明为f(anyelement, anyenum)等效于把它声明为f(anyenum, anyenum):两种实际参数必须是相同的枚举类型。

对于多态类型的“common”家族,匹配和推断规则工作与“simple”家族相同,有一个主要区别: 参数的实际类型不需要相同,只要可以隐式地将它们转换为单一的公共类型即可。 common类型的选择遵循与UNION和相关结构相同的规则(参见 第 10.5 节)。 common类型的选择考虑了anycompatibleanycompatiblenonarray输入的实际类型,anycompatiblearray输入的数组元素类型,以及anycompatiblerange输入中的范围子类型。 如果存在 anycompatiblenonarray,那么通用类型必须是非数组类型。 一旦识别了common类型,anycompatibleanycompatiblenonarray位置中的参数将自动转换到该类型,并且anycompatiblearray位置的参数将自动转换到该类型的数组类型。

因为不可能选择只知道子类型的范围类型,使用anycompatiblerange需要使用该类型声明的所有参数具有相同的实际范围类型, 这种类型的子类型与所选的公共类型一致,这样就不需要对范围值进行强制转换。 与anyrange一样,使用anycompatiblerange作为函数结果类型需要有一个anycompatiblerange参数。

注意,没有anycompatibleenum类型。 这样的类型不是很有用,因为通常没有任何隐式的枚举类型的强制转换,这意味着无法为不同的枚举输入解析通用类型。

“simple”和 “common”多态家族代表两组独立的类型变量。考虑例如

CREATE FUNCTION myfunc(a anyelement, b anyelement,
c anycompatible, d anycompatible)
RETURNS anycompatible AS ...

在此函数的实际调用中,前两个输入必须具有完全相同的类型。 最后两个输入必须提升为一个通用类型,但是这个类型不需要与前两个输入的类型有任何关系。结果将具有最后两个输入的共同类型。

一个可变函数(可以有可变数量的参数,如第 37.5.5 节中所述)能够是多态的: 这可以通过声明其最后一个参数为VARIADICanyarrayVARIADICanycompatiblearray来实现。 为了匹配和决定实际结果类型的参数,这样一种函数的行为和写了合适数量的anynonarrayanycompatiblenonarray参数是一样的。