↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 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 / 7.0 / 6.5 / 6.4
历史版本PostgreSQL 8.3 已于 2013 年 2 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

CREATE TYPE

CREATE TYPE — 定义一种新的数据类型

大纲

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

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

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 ]
    [ , INTERNALLENGTH = { internallength | VARIABLE } ]
    [ , PASSEDBYVALUE ]
    [ , ALIGNMENT = alignment ]
    [ , STORAGE = storage ]
    [ , DEFAULT = default ]
    [ , ELEMENT = element ]
    [ , DELIMITER = delimiter ]
)

CREATE TYPE name

描述

CREATE TYPE在当前数据库中注册一种新的数据 类型。定义该类型的用户将成为其拥有者。

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

复合类型

第一种形式的CREATE TYPE创建一个复合类型。复合类型由属性名和数据类型的列表指定。它本质上与表的行类型相同,但如果想要的只是定义一种类型,使用CREATE TYPE可以避免创建一个实际的表。独立的复合类型可用作函数的参数类型或返回类型。

枚举类型

如第 8.7 节中所述,第二种形式的 CREATE TYPE创建枚举(enum)类型。枚举类 型接受一个 带引号标签的列表,其中每个标签的长度都必须小于 NAMEDATALEN字节(在标准 PostgreSQL构建中为 64 字节)。

基础类型

第四种形式的CREATE TYPE创建一种新的基础类 型(标量类型)。这些参数可以按任意顺序出现,不必局限于上面展示的顺序,而且大多数 都是可选的。在定义该类型之前,必须先注册两个或更多函数(使用 CREATE FUNCTION)。支持函数 input_function和 output_function是必 需的;receive_function、 send_function、 type_modifier_input_function、 type_modifier_output_function、 analyze_function则 是可选的。通常这些函数必须用 C 或其他低级语言编写。

input_function将 类型的外部文本表示转换为该类型的操作符和函数所使用的内部表 示。output_function 执行相反的转换。输入函数可以声明为接受一个cstring 参数,或者接受三个参数,类型分别为cstring、 oid、integer。第一个参数是以 C 字符 串表示的输入文本,第二个参数是该类型自身的 OID(数组类型例外,此 时传入的是其元素类型的 OID),第三个参数是在已知情况下目标列的 typmod(未知则传入 -1)。输入函数必须返回该 数据类型本身的值。通常输入函数应声明为 STRICT;否则,在读取 NULL 输入值时会以 NULL 作为第一个参数调用它。除非函数抛出错误,否则在 这种情况下仍必须返回 NULL。(这种情况主要是为了支持可能需要拒绝 NULL 输入的域输入函数。)输出函数必须声明为接受一个新数据类型参 数,并且必须返回cstring类型。对于 NULL 值不会调用 输出函数。

可选的receive_function 把类型的外部二进制表示转换为内部表示。如果未提供此函数,该类型就 不能参与二进制输入。外部二进制表示应选择为既能低成本转换为内部形 式,又具有合理可移植性。(例如,标准整数数据类型把网络字节序用作 外部二进制表示,而内部表示则使用机器的本地字节序。) 接收函数 应进行充分检查以确保值有效。它可以声明为接受一个 internal参数,或者接受三个参数,类型分别为 internal、oid、integer。 第一个参数是指向保存已接收字节串的StringInfo缓冲区 的指针;其余可选参数与文本输入函数相同。接收函数必须返回该数据类 型本身的值。通常,接收函数应声明为 STRICT;否则,在读取 NULL 输 入值时会以 NULL 作为第一个参数调用它。除非函数抛出错误,否则在这 种情况下仍必须返回 NULL。(这种情况主要是为了支持可能需要拒绝 NULL 输入的域接收函数。)类似地,可选的 send_function把内 部表示转换为外部二进制表示。如果未提供此函数,该类型就不能参与二 进制输出。发送函数必须声明为接受一个新数据类型参数,并且必须返回 bytea类型。对于 NULL 值不会调用发送函数。

读到这里,你可能会问:既然新类型本身还没创建,输入和输出函数怎么 能声明为返回或接受这个新类型呢?答案是,应先把该类型定义为一种 shell 类型,它除了名称和拥有者外没有任何 属性,只是一个占位符类型。这可通过执行不带任何附加参数的命令 CREATE TYPE name 完成。然后就可以定义引用该 shell 类型的 I/O 函数。最后,再用带 完整定义的CREATE TYPE替换该 shell 条目,生 成一个完整且有效的类型定义,此后新类型就能正常使用。

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

可选的analyze_function 为该数据类型的列执行类型专用的统计信息收集。默认情况下,如果该类 型有默认的 B-树操作符类,ANALYZE将尝试使用 该类型的“equals”和“less-than”操作符收 集统计信息。对于非标量类型,这种行为很可能不合适,因此可以通过指 定自定义分析函数来覆盖。分析函数必须声明为接受一个 internal参数并返回boolean结果。分 析函数的详细 API 见src/include/commands/vacuum.h。

虽然新类型内部表示的细节只有 I/O 函数以及你为该类型编写的其他函 数才知道,但仍有若干内部表示属性必须向 PostgreSQL声明。其中最重要的是 internallength。 基础类型可以是定长的,此时internallength为正整数;也可 以是变长的,此时将internallength设为 VARIABLE。(在内部,这通过把 typlen设为 -1 表示。)所有变长类型的内部表 示都必须以一个 4 字节整数开头,用来给出该类型该值的总长度。

可选标志PASSEDBYVALUE表示该数据类型的值按值 传递,而不是按引用传递。内部表示大于Datum类型大小 (多数机器上为 4 字节,少数为 8 字节)的类型不能按值传递。

alignment参数指定该 数据类型所需的存储对齐方式。允许的值分别对应按 1、2、4 或 8 字节 边界对齐。注意,变长类型的对齐至少必须为 4,因为它们的第一个组成 部分必然是一个int4。

storage参数允许为 变长数据类型选择存储策略。(定长类型只允许 plain。)plain表示该类型 数据始终内联存储且不压缩。extended表示系统 会先尝试压缩较长的数据值,如果仍然过长,就把该值移出主表行。 external允许把值移出主表,但系统不会尝试压缩 它。main允许压缩,但不鼓励把值移出主表。(采 用这种存储策略的数据项在没有其他办法让一行适配时仍可能被移出主 表,但与extended和external 数据项相比,它们会被优先保留在主表中。)

如果用户希望该数据类型列的默认值不是空值,可以指定默认值。用 DEFAULT关键字指定默认值。(该默认值可以被附 加到具体列上的显式DEFAULT子句覆盖。)

若要表明一种类型是数组类型,可用ELEMENT 关键字指定数组元素的类型。例如,要定义由 4 字节整数 (int4)构成的数组,可指定 ELEMENT = int4。更多关于数组类型的细节见下文。

若要指定该类型数组在外部表示中用于分隔各值的分隔符,可把 delimiter设为特定 字符。默认分隔符是逗号(,)。注意,这个分隔 符关联的是数组元素类型,而不是数组类型本身。

数组类型

每当创建用户定义类型时,PostgreSQL 都会自动创建一个关联的数组类型,其名称由元素类型名前加一个下划线 组成;必要时还会截断,以保持其长度小于 NAMEDATALEN字节。(如果这样生成的名称与现有 类型名冲突,就会重复这一过程,直到找到不冲突的名称。)这种隐式 创建的数组类型是变长的,并使用内置输入/输出函数 array_in和array_out。 该数组类型会跟踪其元素类型的拥有者 或模式的任何变化,并在元素类型被删除时一并删除。

如果系统会自动创建正确的数组类型,你可能会合理地问,为什么还需 要ELEMENT选项。ELEMENT唯一有用的情况是:你正在创建一种定长类型,而它在内部恰好是若干相同元素 组成的数组,并且除了为整个类型提供的操作之外,你还希望允许通过下 标直接访问这些元素。例如,类型point在内部就表示为 两个浮点数,可以用point[0]和 point[1]访问。注意,这种机制只适用于内部形 式恰好是一串相同定长字段的定长类型。可用下标访问的变长类型必须采用array_in和array_out使用的通用内部表示。由于历史原因(也就是说这显然 不对,但现在改已经太晚了),定长数组类型的下标从零开始,而变长数 组则从一开始。

参数

name

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

attribute_name

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

data_type

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

label

表示枚举类型某个值所关联文本标签的字符串字面量。

input_function

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

output_function

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

receive_function

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

send_function

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

type_modifier_input_function

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

type_modifier_output_function

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

analyze_function

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

internallength

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

alignment

该数据类型的存储对齐需求。如果被指定,它必须是 char、int2、 int4或者double。默认是 int4。

storage

该数据类型的存储策略。如果被指定,必须是 plain、external、 extended或者main。 默认是plain。

default

该数据类型的默认值。若省略,默认值为 null。

element

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

delimiter

在由该类型构成的数组中各值之间使用的分隔符字符。

注解

由于数据类型一旦创建,其使用方式就不再受限制,因此创建基础类型相 当于对类型定义中提到的那些函数授予公共执行权限。(因此要求类型的 创建者拥有这些函数。)这对适合用于类型定义的那类函数来说通常不是 问题。但如果设计一种类型 时,需要在把它转换为外部形式或从外部形式转换回来时使用 “秘密”信息,就应当三思。

在PostgreSQL 8.3 之前,自动生成的数 组类型名称总是恰好等于元素类型名称前加一个下划线字符 (_)。(因此,类型名称的长度限制比其他名称少 一个字符。)虽然现在通常仍是这样,但在名称达到最大长度或与以下划 线开头的用户类型名冲突时,数组类型名称可能与此不同。因此,依赖这 一约定编写代码的做法已经弃用。请改用 pg_type.typarray 来定位与给定类型关联的数组类型。

建议避免使用以下划线开头的类型名和表名。虽然服务器会改变生成的数组 类型名称以避免与用户给定的名称冲突,但仍然存在混淆风险,特别是对 旧客户端软件而言,它们可能会假定以下划线开头的类型名总是表示数 组。

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

在PostgreSQL 7.3 以前的版本中,通常完全不创建 shell 类型,而是将函数中对类型名的前向引用替换为占位伪类型 opaque。在 7.3 以前,cstring参数和结果也必须声明为opaque。为了支持载入旧转储文件,CREATE TYPE会接受使用opaque声明的 I/O 函数,但会发出通知,并修改函数声明以使用正确的类型。

示例

这个示例创建一种复合类型,并在函数定义中使用它:

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

这个示例创建基础类型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
);

更多示例(包括配套的输入和输出函数)请见第 34.11 节。

兼容性

这里的CREATE TYPE命令是一个PostgreSQL扩展。SQL标准中也有一个CREATE TYPE语句,但细节上相当不同。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。