2024 年 9 月 26 日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不受支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

CREATE TYPE

CREATE TYPE — 定义新的数据类型

概要

CREATE TYPE name AS
    ( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] )

CREATE TYPE name AS ENUM
    ( [ 'label' [, ... ] ] )

CREATE TYPE name AS RANGE (
    SUBTYPE = subtype
    [ , SUBTYPE_OPCLASS = subtype_operator_class ]
    [ , COLLATION = collation ]
    [ , CANONICAL = canonical_function ]
    [ , SUBTYPE_DIFF = subtype_diff_function ]
    [ , MULTIRANGE_TYPE_NAME = multirange_type_name ]
)

CREATE TYPE name (
    INPUT = input_function,
    OUTPUT = output_function
    [ , RECEIVE = receive_function ]
    [ , SEND = send_function ]
    [ , TYPMOD_IN = type_modifier_input_function ]
    [ , TYPMOD_OUT = type_modifier_output_function ]
    [ , ANALYZE = analyze_function ]
    [ , SUBSCRIPT = subscript_function ]
    [ , INTERNALLENGTH = { internallength | VARIABLE } ]
    [ , PASSEDBYVALUE ]
    [ , ALIGNMENT = alignment ]
    [ , STORAGE = storage ]
    [ , LIKE = like_type ]
    [ , CATEGORY = category ]
    [ , PREFERRED = preferred ]
    [ , DEFAULT = default ]
    [ , ELEMENT = element ]
    [ , DELIMITER = delimiter ]
    [ , COLLATABLE = collatable ]
)

CREATE TYPE name

描述

CREATE TYPE 注册一个新的数据类型供当前数据库使用。定义类型的用户成为其所有者。

如果给出了模式名,则类型将在指定的模式中创建。否则,它将在当前模式中创建。类型名称必须与同一模式中任何现有类型或域的名称不同。(由于表有相关联的数据类型,因此类型名称还必须与同一模式中任何现有表的名称不同。)

CREATE TYPE 有五种形式,如上面的语法概要所示。它们分别创建一个 组合类型、一个 枚举类型、一个 范围类型、一个 基础类型或一个 空类型。下面将依次讨论前四种类型。空类型只是以后要定义的类型的占位符;它通过在没有参数的情况下发出 CREATE TYPE 来创建,除了类型名。如这些部分所述,在创建范围类型和基本类型时需要空类型作为前向引用。

组合类型

CREATE TYPE 的第一种形式创建一个组合类型。组合类型由属性名称和数据类型列表指定。如果属性的数据类型是可排序的,则也可以指定属性的排序规则。组合类型本质上与表的行类型相同,但使用 CREATE TYPE 可以避免在只需要定义类型时创建实际表。例如,独立组合类型可作为函数的参数或返回值类型。

要创建组合类型,您必须对所有属性类型具有 USAGE 权限。

枚举类型

CREATE TYPE 的第二种形式创建一个枚举(enum)类型,如 第 8.7 节 中所述。枚举类型采用带引号的标签列表,每个标签的长度必须小于 NAMEDATALEN 字节(在标准 PostgreSQL 构建中为 64 字节)。(可以创建没有标签的枚举类型,但这种类型在至少添加了一个标签之前不能用于保存值,可以使用 ALTER TYPE。)

范围类型

CREATE TYPE 的第三种形式创建一个新的范围类型,如 第 8.17 节 中所述。

范围类型的 subtype 可以是任何与 b 树操作符类关联的类型(以确定范围类型值的排序)。通常,子类型的默认 b 树操作符类用于确定排序;要使用非默认操作符类,请使用 subtype_opclass 指定其名称。如果子类型是可排序的,并且您要在范围的排序中使用非默认排序规则,请使用 collation 选项指定所需的排序规则。

可选的 canonical 函数必须接受一个定义范围类型的参数,并返回相同类型的返回值。当适用时,它用于将范围值转换为规范形式。有关更多信息,请参阅 第 8.17.8 节。创建 canonical 函数有点棘手,因为它必须在声明范围类型之前定义。为此,您必须首先创建一个空类型,它只是一个没有属性的占位符类型,除了名称和所有者。这是通过发出命令 CREATE TYPE name 来完成的,没有任何其他参数。然后可以使用空类型作为参数和结果来声明函数,最后可以使用相同的名称声明范围类型。这会自动用有效的范围类型替换空类型条目。

可选的 subtype_diff 函数必须接受 subtype 类型的值作为参数,并返回一个 double precision 值,表示两个给定值之间的差异。虽然这是可选的,但提供它可以显著提高范围类型列上 GiST 索引的效率。有关更多信息,请参阅 第 8.17.8 节

可选的 multirange_type_name 参数指定相应多范围类型的名称。如果没有指定,则会自动选择此名称,如下所示。如果范围类型名称包含子字符串 range,则多范围类型名称通过将范围类型名称中的 range 子字符串替换为 multirange 来形成。否则,多范围类型名称通过在范围类型名称后附加 _multirange 后缀来形成。

基础类型

CREATE TYPE 的第四种形式创建一个新的基础类型(标量类型)。要创建新的基础类型,您必须是超级用户。(实施此限制是因为错误的类型定义可能会混淆甚至使服务器崩溃。)

参数可以按任何顺序出现,而不仅仅是上面所示的顺序,大多数参数是可选的。您必须在定义类型之前注册两个或多个函数(使用 CREATE FUNCTION)。支持函数 input_functionoutput_function 是必需的,而函数 receive_functionsend_functiontype_modifier_input_functiontype_modifier_output_functionanalyze_functionsubscript_function 是可选的。通常这些函数必须用 C 或其他低级语言编写。

input_function 将类型的外部文本表示形式转换为类型定义的运算符和函数使用的内部表示形式。 output_function 执行反向转换。输入函数可以声明为接受一个类型为 cstring 的参数,或者声明为接受三个类型为 cstringoidinteger 的参数。第一个参数是输入文本作为 C 字符串,第二个参数是类型自己的 OID(除了数组类型,数组类型改为接收其元素类型的 OID),第三个参数是目标列的 typmod(如果已知,则传递 -1,如果未知)。输入函数必须返回数据类型本身的值。通常,输入函数应声明为 STRICT;如果它不是,则在读取 NULL 输入值时将使用 NULL 作为第一个参数调用它。在这种情况下,函数仍然必须返回 NULL,除非它引发错误。(这种情况主要是为了支持域输入函数,域输入函数可能需要拒绝 NULL 输入。)输出函数必须声明为接受一个新数据类型的值。输出函数必须返回类型为 cstring。对于 NULL 值,不会调用输出函数。

可选的 receive_function 将类型的外部二进制表示形式转换为内部表示形式。如果没有提供此函数,则类型将无法参与二进制输入。二进制表示形式应选择为易于转换为内部形式,同时具有良好的可移植性。(例如,标准整数数据类型使用网络字节序作为外部二进制表示形式,而内部表示形式使用机器的本机字节序。)接收函数应执行足够的检查以确保值有效。接收函数可以声明为接受一个类型为 internal 的参数,或者声明为接受三个类型为 internaloidinteger 的参数。第一个参数是指向包含接收到的字节字符串的 StringInfo 缓冲区的指针;可选参数与文本输入函数的相同。接收函数必须返回数据类型本身的值。通常,接收函数应声明为 STRICT;如果它不是,则在读取 NULL 输入值时将使用 NULL 作为第一个参数调用它。在这种情况下,函数仍然必须返回 NULL,除非它引发错误。(这种情况主要是为了支持域接收函数,域接收函数可能需要拒绝 NULL 输入。)类似地,可选的 send_function 将从内部表示形式转换为外部二进制表示形式。如果没有提供此函数,则类型将无法参与二进制输出。发送函数必须声明为接受一个新数据类型的值。发送函数必须返回类型为 bytea。对于 NULL 值,不会调用发送函数。

此时您应该想知道如何声明输入和输出函数的结果或参数为新类型,因为它们必须在新类型创建之前创建。答案是,类型应首先定义为 空类型,它只是一个没有属性的占位符类型,除了名称和所有者。这是通过发出命令 CREATE TYPE name 来完成的,没有任何其他参数。然后可以定义引用空类型的 C I/O 函数。最后,CREATE TYPE 的完整定义会用完整的、有效的类型定义替换空类型条目,之后新类型就可以正常使用。

如果类型支持修饰符,则需要可选的 type_modifier_input_functiontype_modifier_output_function,即附加到类型声明的可选约束,例如 char(5)numeric(30,2)PostgreSQL 允许用户定义的类型接受一个或多个简单常量或标识符作为修饰符。 但是,此信息必须能够打包成单个非负整数,以便存储在系统目录中。 type_modifier_input_functioncstring 数组的形式传递声明的修饰符。 它必须检查值的有效性(如果值错误则抛出错误),如果值正确,则返回一个将存储为列 typmod 的单个非负 integer 值。 如果类型没有 type_modifier_input_function,则会拒绝类型修饰符。 type_modifier_output_function 将内部整数 typmod 值转换回用户显示的正确形式。 它必须返回一个 cstring 值,该值是附加到类型名称的确切字符串;例如 numeric 的函数可能返回 (30,2)。 允许省略 type_modifier_output_function,在这种情况下,默认显示格式只是存储的 typmod 整数值,用括号括起来。

可选的 analyze_function 为数据类型列执行特定于类型的统计信息收集。 默认情况下,ANALYZE 将尝试使用类型的 equalsless-than 运算符来收集统计信息,如果类型存在默认的 b 树运算符类。 对于非标量类型,此行为可能不适合,因此可以通过指定自定义分析函数来覆盖它。 分析函数必须声明为接受一个类型为 internal 的参数,并返回一个 boolean 结果。 分析函数的详细 API 位于 src/include/commands/vacuum.h 中。

可选的 subscript_function 允许数据类型在 SQL 命令中使用下标。 指定此函数不会使类型被视为 true 数组类型;例如,它不会成为 ARRAY[] 结构的返回类型候选。 但是,如果对类型的值的加下标是提取数据的一种自然表示法,则可以编写一个 subscript_function 来定义其含义。 下标函数必须声明为接受一个类型为 internal 的参数,并返回一个 internal 结果,它是一个指向实现下标的方法(函数)结构的指针。 下标函数的详细 API 位于 src/include/nodes/subscripting.h 中。 您可能还需要阅读 src/backend/utils/adt/arraysubs.c 中的数组实现,或者 contrib/hstore/hstore_subs.c 中的更简单代码。 更多信息请参见下面的 数组类型

虽然新类型的内部表示的细节只有 I/O 函数和其他您创建的用于处理类型的函数才知道,但必须向 PostgreSQL 声明内部表示的几个属性。 其中最重要的属性是 internallength。 基本数据类型可以是固定长度,在这种情况下,internallength 是一个正整数,或者可变长度,通过将 internallength 设置为 VARIABLE 来指示。(在内部,这通过将 typlen 设置为 -1 来表示。)所有可变长度类型的内部表示都必须以一个 4 字节整数开头,该整数给出该类型此值的总长度。(请注意,长度字段通常被编码,如 第 65.2 节 中所述;直接访问它是不明智的。)

可选标志 PASSEDBYVALUE 表示此数据类型的值按值传递,而不是按引用传递。 按值传递的类型必须是固定长度的,并且它们的内部表示不能大于 Datum 类型的尺寸(在某些机器上为 4 字节,在其他机器上为 8 字节)。

alignment 参数指定数据类型所需的存储对齐方式。 允许的值等效于 1、2、4 或 8 字节边界上的对齐。 请注意,可变长度类型必须具有至少 4 的对齐方式,因为它们必然包含一个 int4 作为它们的第一个组件。

storage 参数允许为可变长度数据类型选择存储策略。(对于固定长度类型,只允许 plain。) plain 指定该类型的将始终内联存储,不会压缩。 extended 指定系统将首先尝试压缩一个长数据值,如果它仍然太长,则会将该值从主表行中移动。 external 允许将值从主表中移动,但系统不会尝试压缩它。 main 允许压缩,但阻止将值从主表中移动。(具有此存储策略的数据项仍然可能从主表中移动,如果别无选择无法使行适合,但它们将在主表中优先于 extendedexternal 项。)

plain 之外的所有 storage 值都暗示数据类型的函数可以处理已被 toasted 的值,如 第 65.2 节第 36.13.1 节 中所述。 给出的特定其他值仅仅决定可调式数据类型的列的默认 TOAST 存储策略;用户可以使用 ALTER TABLE SET STORAGE 为单个列选择其他策略。

like_type 参数提供了一种替代方法来指定数据类型的基本表示属性:从某个现有类型复制它们。 internallengthpassedbyvaluealignmentstorage 的值将从指定的类型复制。(虽然通常不可取,但可以覆盖其中一些值,方法是在 LIKE 子句中指定它们。)当新类型的底层实现以某种方式 搭载 在现有类型上时,以这种方式指定表示尤其有用。

categorypreferred 参数可用于帮助控制在模糊情况下将应用哪个隐式转换。 每个数据类型都属于一个类别,该类别由一个 ASCII 字符命名,并且每个类型在其类别中是 首选 或不是。 当此规则有助于解决重载函数或运算符时,解析器将优先转换为首选类型(但仅从同一类别中的其他类型转换)。 有关更多详细信息,请参见 第 10 章。 对于没有从其他类型进行隐式转换的类型,将这些设置保留在默认值就足够了。 但是,对于一组相关的类型,它们具有隐式转换,通常将它们全部标记为属于一个类别并将一个或两个 最通用 类型标记为在类别中是首选类型是有帮助的。 category 参数在将用户定义的类型添加到现有的内置类别(例如数值或字符串类型)时特别有用。 但是,也可以创建全新的用户定义的类型类别。 选择除大写字母以外的任何 ASCII 字符来命名此类类别。

可以指定一个默认值,以防用户希望数据类型的列默认为除空值以外的值。 使用 DEFAULT 关键字指定默认值。(这样的默认值可以被附加到特定列的显式 DEFAULT 子句覆盖。)

要指示类型是固定长度的数组类型,请使用 ELEMENT 关键字指定数组元素的类型。 例如,要定义一个由 4 字节整数 (int4) 组成的数组,请指定 ELEMENT = int4。 有关更多详细信息,请参见下面的 数组类型

要指示此类型数组的外部表示中值之间使用的分隔符,delimiter 可以设置为特定字符。 默认分隔符是逗号 (,)。 请注意,分隔符与数组元素类型相关联,而不是与数组类型本身相关联。

如果可选的布尔参数 collatable 为 true,则类型的列定义和表达式可以通过使用 COLLATE 子句来携带排序规则信息。 处理类型的函数的实现是否实际使用排序规则信息取决于具体情况;这不会仅仅通过将类型标记为可排序而自动发生。

数组类型

每当创建一个用户定义的类型时,PostgreSQL 都会自动创建一个关联的数组类型,其名称由元素类型名称前缀加上下划线组成,并在必要时截断,以使其长度小于 NAMEDATALEN 字节。(如果生成的名称与现有类型名称冲突,则重复此过程,直到找到一个不冲突的名称。)此隐式创建的数组类型是可变长度的,并使用内置的输入和输出函数 array_inarray_out。 此外,此类型是系统用于用户定义类型上的 ARRAY[] 等构造的类型。 数组类型跟踪其元素类型的拥有者或模式的任何更改,如果元素类型被删除,则也会被删除。

您可能会合理地问,如果系统自动创建正确的数组类型,为什么会有 ELEMENT 选项。 使用 ELEMENT 的主要情况是,当您创建一种固定长度的类型时,它恰好是多个相同事物的内部数组,并且您希望允许通过加下标直接访问这些事物,除了您计划为整个类型提供的任何操作之外。 例如,类型 point 被表示为两个浮点数,可以使用 point[0]point[1] 来访问。 请注意,此功能仅适用于内部形式恰好是相同固定长度字段序列的固定长度类型。 由于历史原因(即,这显然是错误的,但现在改变它为时已晚),固定长度数组类型的加下标从零开始,而不是像可变长度数组一样从一开始。

指定 SUBSCRIPT 选项允许对数据类型进行加下标,即使系统不将其视为数组类型。 之前针对固定长度数组描述的行为实际上是通过 SUBSCRIPT 处理程序函数 raw_array_subscript_handler 实现的,如果您为固定长度类型指定了 ELEMENT 但没有编写 SUBSCRIPT,则会自动使用此函数。

当指定自定义 SUBSCRIPT 函数时,无需指定 ELEMENT,除非 SUBSCRIPT 处理程序函数需要查询 typelem 来找出要返回的内容。 请注意,指定 ELEMENT 会导致系统假设新类型包含元素类型或以某种方式在物理上依赖于元素类型;因此,例如,如果存在任何依赖类型的列,则不允许更改元素类型的属性。

参数

name

要创建的类型的名称(可选地是模式限定的)。

attribute_name

复合类型的属性(列)名称。

data_type

现有数据类型名称,将成为复合类型中的一个列。

collation

现有排序规则的名称,将与复合类型的列或范围类型相关联。

label

一个字符串字面量,表示与枚举类型的一个值相关联的文本标签。

subtype

范围类型将代表其范围的元素类型的名称。

subtype_operator_class

子类型的 B 树操作符类的名称。

canonical_function

范围类型的规范化函数的名称。

subtype_diff_function

子类型差异函数的名称。

multirange_type_name

相应的多范围类型的名称。

input_function

将数据从类型外部文本形式转换为其内部形式的函数的名称。

output_function

将数据从类型的内部形式转换为其外部文本形式的函数的名称。

receive_function

将数据从类型的外部二进制形式转换为其内部形式的函数的名称。

send_function

将数据从类型的内部形式转换为其外部二进制形式的函数的名称。

type_modifier_input_function

将类型的修饰符数组转换为内部形式的函数的名称。

type_modifier_output_function

将类型的修饰符的内部形式转换为外部文本形式的函数的名称。

analyze_function

执行数据类型统计分析的函数的名称。

subscript_function

定义对数据类型的值进行下标操作的函数的名称。

internallength

一个数值常量,指定新类型内部表示的字节长度。默认假设是可变长度。

alignment

数据类型的存储对齐要求。如果指定,它必须是 charint2int4double;默认值为 int4

storage

数据类型的存储策略。如果指定,必须是 plainexternalextendedmain;默认值为 plain

like_type

现有数据类型的名称,新类型将具有与其相同的表示形式。 internallengthpassedbyvaluealignmentstorage 的值将从该类型复制,除非在该 CREATE TYPE 命令中的其他地方明确指定。

category

此类型的类别代码(单个 ASCII 字符)。默认值为 'U',表示“用户定义类型”。其他标准类别代码可以在 表 51.65 中找到。您也可以选择其他 ASCII 字符以创建自定义类别。

preferred

如果此类型是其类型类别中的首选类型,则为真,否则为假。默认值为假。在创建现有类型类别中的新首选类型时要格外小心,因为这可能会导致意外的行为变化。

default

数据类型的默认值。如果省略,默认值为 null。

element

正在创建的类型是一个数组;这指定了数组元素的类型。

delimiter

由该类型的数组中值之间的分隔符字符。

collatable

如果此类型的操作可以使用排序信息,则为真。默认值为假。

说明

因为在创建数据类型后,对它的使用没有任何限制,所以创建基本类型或范围类型等同于授予类型定义中提到的函数的公共执行权限。对于类型定义中使用的那种函数,这通常不是问题。但是,在设计类型时,您可能要三思而后行,因为在将其转换为或从外部形式转换时,可能需要使用“秘密”信息。

在 PostgreSQL 版本 8.3 之前,生成数组类型的名称始终是元素类型的名称,前面添加一个下划线字符 (_)。(因此,类型名称的长度限制为其他名称的长度减一。)虽然这种情况通常仍然如此,但在最大长度名称或与以下划线开头的用户类型名称发生冲突的情况下,数组类型名称可能会有所不同。因此,编写依赖于此约定的代码已不被建议。相反,请使用 pg_type.typarray 来查找与给定类型关联的数组类型。

建议避免使用以下划线开头的类型和表名。虽然服务器会更改生成的数组类型名称以避免与用户给定的名称发生冲突,但仍然存在混淆的风险,尤其是对于旧的客户端软件,它们可能假设以下划线开头的类型名称始终代表数组。

在 PostgreSQL 版本 8.2 之前,shell 类型创建语法 CREATE TYPE name 不存在。创建新基本类型的方法是先创建它的输入函数。在这种方法中,PostgreSQL 将首先看到新数据类型的名称作为输入函数的返回值。shell 类型在这种情况下是隐式创建的,然后它可以在其余 I/O 函数的定义中引用。这种方法仍然有效,但已被弃用,并且可能在将来的某个版本中被禁止。此外,为了避免由于函数定义中的简单拼写错误而意外地用 shell 类型填充目录,只有在输入函数是用 C 编写的时,才会以这种方式创建 shell 类型。

在 PostgreSQL 版本 16 及更高版本中,基本类型的输入函数最好使用新的 errsave()/ereturn() 机制返回“软”错误,而不是像以前版本那样抛出 ereport() 异常。有关更多信息,请参见 src/backend/utils/fmgr/README

示例

本示例创建了一个复合类型,并在函数定义中使用它。

CREATE TYPE compfoo AS (f1 int, f2 text);

CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS $$
    SELECT fooid, fooname FROM foo
$$ LANGUAGE SQL;

本示例创建了一个枚举类型,并在表定义中使用它。

CREATE TYPE bug_status AS ENUM ('new', 'open', 'closed');

CREATE TABLE bug (
    id serial,
    description text,
    status bug_status
);

本示例创建了一个范围类型。

CREATE TYPE float8_range AS RANGE (subtype = float8, subtype_diff = float8mi);

本示例创建了基本数据类型 box,然后在表定义中使用该类型。

CREATE TYPE box;

CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;

CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function
);

CREATE TABLE myboxes (
    id integer,
    description box
);

如果 box 的内部结构是四个 float4 元素的数组,我们可能可以使用以下方法:

CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function,
    ELEMENT = float4
);

这将允许通过对 box 值的组件数字进行下标操作来访问它们。否则,该类型将与之前一样。

本示例创建了一个大型对象类型,并在表定义中使用它。

CREATE TYPE bigobj (
    INPUT = lo_filein, OUTPUT = lo_fileout,
    INTERNALLENGTH = VARIABLE
);
CREATE TABLE big_objs (
    id integer,
    obj bigobj
);

更多示例,包括合适的输入和输出函数,请参见 第 36.13 节

兼容性

第一种形式的 CREATE TYPE 命令,它创建了一个复合类型,符合SQL标准。其他形式是 PostgreSQL 的扩展。在SQL标准中,CREATE TYPE 语句还定义了其他未在 PostgreSQL 中实现的形式。

能够创建具有零个属性的复合类型是 PostgreSQL 对标准的特定于产品偏差(类似于 CREATE TABLE 中的情况)。

提交更正

如果您在文档中发现任何不正确的信息、与您对特定功能的体验不符或需要进一步说明,请使用 此表格 报告文档问题。