# 创建类型

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

# 描述

创建类型注册一个新的数据类型以在当前数据库中使用。定义类型的用户成为其所有者。

如果给定模式名称,则在指定模式中创建类型。否则,它会在当前模式中创建。类型名称必须与同一架构中任何现有类型或域的名称不同。(因为表具有关联的数据类型,类型名称也必须与同一架构中任何现有表的名称不同。)

有五种形式创建类型,如上面的语法概要所示。他们分别创建了一个复合型, 一个枚举类型, 一种范围类型, 一种基本类型, 或外壳类型.下面依次讨论其中的前四个。shell 类型只是稍后定义的类型的占位符;它是通过发行创建的创建类型除了类型名称之外没有其他参数。在创建范围类型和基本类型时,需要外壳类型作为前向引用,如这些部分中所述。

# 复合类型

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

为了能够创建复合类型,您必须具有用法所有属性类型的特权。

# 枚举类型

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

# 范围类型

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

范围类型的*亚型可以是具有关联 b-tree 运算符类的任何类型(以确定范围类型的值的顺序)。通常,子类型的默认 b-tree 运算符类用于确定排序;要使用非默认运算符类,请指定其名称subtype_opclass.如果子类型是可排序的,并且您想在范围的排序中使用非默认排序规则,请使用整理*选项。

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

可选的*subtype_diff函数必须取两个值亚型*类型作为参数,并返回一个双精度value 表示两个给定值之间的差异。虽然这是可选的,但提供它可以提高范围类型列上的 GiST 索引的效率。看第 8.17.8 节了解更多信息。

可选的*多范围类型名称*参数指定对应的多范围类型的名称。如果未指定,则按如下方式自动选择此名称。如果范围类型名称包含子字符串范围,则多范围类型名称通过替换范围子串多量程在范围类型名称中。否则,多范围类型名称通过附加一个_多范围范围类型名称的后缀。

# 基本类型

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

参数可以以任何顺序出现,不仅如上所示,而且大多数是可选的。您必须注册两个或更多功能(使用创建函数) 在定义类型之前。支持功能*输入函数输出函数是必需的,而功能接收函数,发送函数,type_modifier_input_function,type_modifier_output_function,分析函数, 和下标函数*是可选的。通常,这些函数必须用 C 或其他低级语言编码。

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

可选的*接收函数将类型的外部二进制表示转换为内部表示。如果未提供此函数,则该类型不能参与二进制输入。应该选择二进制表示以便宜地转换为内部形式,同时具有合理的可移植性。(例如,标准整数数据类型使用网络字节顺序作为外部二进制表示,而内部表示是机器的本机字节顺序。)接收函数应该执行足够的检查以确保该值有效。接收函数可以声明为接受一个类型的参数内部的, 或采用三个类型的参数内部的,样的,整数.第一个参数是一个指向字符串信息保存接收到的字节串的缓冲区;可选参数与文本输入函数相同。接收函数必须返回数据类型本身的值。通常,接收函数应声明为 STRICT;如果不是,它将在读取 NULL 输入值时使用 NULL 第一个参数调用。在这种情况下,该函数仍必须返回 NULL,除非它引发错误。(这种情况主要是为了支持域接收函数,这可能需要拒绝 NULL 输入。)同样,可选的发送函数*从内部表示转换为外部二进制表示。如果不提供此函数,则该类型不能参与二进制输出。发送函数必须声明为采用新数据类型的一个参数。发送函数必须返回类型拜茶.不会为 NULL 值调用发送函数。

此时您应该想知道如何将输入和输出函数声明为具有新类型的结果或参数,而在创建新类型之前必须先创建它们。答案是首先应该将类型定义为外壳类型,这是一个占位符类型,除了名称和所有者之外没有其他属性。这是通过发出命令来完成的创建类型 *姓名*,没有附加参数。然后可以参考 shell 类型定义 C I/O 函数。最后,创建类型with a full definition 将 shell 条目替换为完整、有效的类型定义,之后可以正常使用新类型。

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

可选的*分析函数*对数据类型的列执行类型特定的统计信息收集。默认情况下,分析如果该类型有默认的 b-tree 运算符类,将尝试使用该类型的“等于”和“小于”运算符收集统计信息。对于非标量类型,此行为可能不合适,因此可以通过指定自定义分析函数来覆盖它。分析函数必须声明为采用单个类型的参数内部的,并返回一个布尔值结果。分析函数的详细 API 出现在src/include/commands/vacuum.h.

可选的*下标函数允许数据类型在 SQL 命令中下标。指定此函数不会导致该类型被视为“真实”数组类型;例如,它不会是结果类型的候选者大批[]结构体。但是,如果下标类型的值是从中提取数据的自然符号,那么下标函数*可以写来定义这意味着什么。下标函数必须声明为接受一个类型的参数内部的,并返回一个内部的result,它是一个指向实现下标的方法(函数)结构的指针。下标函数的详细 API 出现在src/include/nodes/subscripting.h.阅读中的数组实现也可能很有用src/backend/utils/adt/arraysubs.c,或者更简单的代码贡献/hstore/hstore_subs.c.附加信息出现在数组类型以下。

虽然新类型的内部表示的细节只有 I/O 函数和您创建以使用该类型的其他函数知道,但内部表示的几个属性必须向 PostgreSQL 声明。其中最重要的是*内部长度.基本数据类型可以是固定长度的,在这种情况下内部长度是一个正整数或可变长度,由设置指示内部长度*到多变的.(在内部,这由设置表示泰普伦到-1.)所有可变长度类型的内部表示必须以一个 4 字节整数开头,给出该类型值的总长度。(请注意,长度字段通常是编码的,如第 70.2 节;直接访问它是不明智的。)

可选标志传递价值表示此数据类型的值是按值传递的,而不是按引用传递的。按值传递的类型必须是定长的,其内部表示不能大于基准类型(在某些机器上为 4 个字节,在其他机器上为 8 个字节)。

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

这*贮存*参数允许为可变长度数据类型选择存储策略。(仅有的清楚的允许用于固定长度类型。)清楚的指定该类型的数据将始终在线存储而不是压缩。扩展的指定系统将首先尝试压缩长数据值,如果仍然太长,则将该值移出主表行。外部的允许将值移出主表,但系统不会尝试压缩它。主要的允许压缩,但不鼓励将值移出主表。(如果没有其他方法使行适合,具有此存储策略的数据项可能仍会移出主表,但它们将优先保留在主表中扩展的外部的项目。)

全部*贮存以外的值清楚的暗示该数据类型的函数可以处理已被烤*,如中所述第 70.2 节第 38.13.1 节.给定的特定其他值仅确定可烘烤数据类型的列的默认 TOAST 存储策略;用户可以使用为各个列选择其他策略更改表集存储.

这*like_type参数提供了另一种方法来指定数据类型的基本表示属性:从某个现有类型复制它们。的价值观内部长度,按值传递,结盟, 和贮存*从命名类型复制。(虽然通常是不可取的,但可以通过指定这些值以及子句。)当新类型的低级实现以某种方式在现有类型上“搭载”时,以这种方式指定表示特别有用。

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

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

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

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

如果可选的布尔参数*可整理的*为真,该类型的列定义和表达式可以通过使用整理条款。实际使用归类信息取决于对类型进行操作的函数的实现;仅仅通过将类型标记为可排序,这不会自动发生。

# 数组类型

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

你可能会合理地问为什么有一个元素选项,如果系统自动生成正确的数组类型。使用它的主要案例要素当你制作一个固定长度的类型时,它恰好在内部是一个由许多相同的东西组成的数组,除了你计划为整个类型提供的任何操作之外,你还想通过订阅直接访问这些东西。例如,输入指向表示为两个浮点数,可以使用点[0]第[1]点。请注意,此功能仅适用于内部形式完全相同的固定长度字段序列的固定长度类型。出于历史原因(也就是说,这显然是错误的,但现在改变它为时已晚),定长数组类型的订阅从零开始,而不是像变长数组那样从一开始。

指定下标选项允许订阅数据类型,即使系统不将其视为数组类型。刚才描述的固定长度数组的行为实际上是由下标处理函数原始数组下标处理程序,如果您指定要素对于固定长度的类型,也不需要书写下标.

指定自定义项时下标函数,则无需指定要素除非下标处理函数需要参考typelem想知道该归还什么。请注意要素使系统假定新类型包含元素类型,或在某种程度上依赖于元素类型;因此,例如,如果存在依赖类型的任何列,则不允许更改元素类型的属性。

# 参数

名称

要创建的类型的名称(可选架构限定)。

属性名称

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

数据类型

要成为复合类型列的现有数据类型的名称。

校勘

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

标签

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

亚型

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

子类型_运算符_类

子类型的b树运算符类的名称。

标准函数

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

子类型_diff_函数

子类型的差异函数的名称。

多范围\类型\名称

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

输入函数

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

输出函数

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

接收函数

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

发送功能

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

类型_修饰符_输入_函数

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

类型_修饰符_输出_函数

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

分析_函数

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

下标函数

定义订阅数据类型值的功能的函数名。

内部长度

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

对齐

数据类型的存储对齐要求。如果指定,则必须烧焦, int2, int4双重的; 默认值是int4.

存储

数据类型的存储策略。如果指定,则必须平原, 外部的, 延长主要的; 默认值是平原.

像型

新类型将与之具有相同表示形式的现有数据类型的名称。价值观*内部长度*, 传递值, *对齐存储*是从该类型复制的,除非被本文档其他地方的显式规范覆盖创建类型命令

类别

此类型的类别代码(单个ASCII字符)。默认值是“你”用于“用户定义类型”。其他标准类别代码可在中找到表52.63。您也可以选择其他ASCII字符来创建自定义类别。

首选

如果此类型是其类型类别中的首选类型,则为True,否则为false。默认值为false。在现有类型类别中创建新的首选类型时要非常小心,因为这可能会导致行为上的惊人变化。

违约

数据类型的默认值。如果省略此选项,则默认值为空。

要素

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

定界符

在这种类型的数组中的值之间使用的分隔符。

可折叠

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

# 笔记

由于创建数据类型后,对其使用没有限制,因此创建基类型或范围类型相当于授予类型定义中提到的函数的公共执行权限。对于类型定义中有用的函数类型来说,这通常不是问题。但是,在设计一个类型之前,您可能需要三思而后行,在将其转换为外部形式或从外部形式转换时,需要使用“机密”信息。

在PostgreSQL版本8.3之前,生成的数组类型的名称始终与元素类型的名称完全相同,只有一个下划线字符(_)准备好了。(因此,类型名称的长度限制为比其他名称少一个字符。)虽然通常情况仍然是这样,但如果名称的最大长度或与以下划线开头的用户类型名称冲突,数组类型名称可能会与此不同。因此,不推荐编写依赖于此约定的代码。相反,使用pg_型.典型阵列查找与给定类型关联的数组类型。

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

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

# 例子

本例创建复合类型并在函数定义中使用:

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);

本例创建基本数据类型然后在表定义中使用该类型:

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
);

如果是一个四人的数组浮动4元素,我们可以使用:

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

这将允许通过订阅来访问框值的组件编号。否则,该类型的行为与之前相同。

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

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

更多示例,包括合适的输入和输出函数,见第38.13节.

# 兼容性

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

创建具有零属性的复合类型的能力是PostgreSQL特定于标准的偏差(类似于创建表格).

# 另见

改变类型, 创建域, 创建函数, 下拉式