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 ] ) 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 ] [ , LIKE = like_type ] [ , CATEGORY = category ] [ , PREFERRED = preferred ] [ , DEFAULT = default ] [ , ELEMENT = element ] [ , DELIMITER = delimiter ] [ , COLLATABLE = collatable ] ) CREATE TYPE name
CREATE TYPE为当前数据库注册一个新的数据类型。 定义该类型的用户成为其所有者。
如果给出模式名,那么该类型是在指定模式中创建。否则它将在当前模式中创建。 类型名必需和同一模式中任何现有的类型或者域不同。(因为表和数据类型有联系, 所以类型名也不能和同模式中的表名字冲突。)
有5种形式的CREATE TYPE,显示在上面的语法摘要里。 他们分别创建复合类型、枚举类型、 范围类型、基本类型或壳类型。 前四个在下面依次讨论。壳类型是简单的一个稍后定义的类型的占位符; 通过发出没有参数只有类型名的CREATE TYPE创建。 当创建范围类型和基本类型时,壳类型作为向前引用需要,在下面章节中讨论。
CREATE TYPE的第一种形式创建一个复合类型。 复合类型是通过一列属性名和数据类型声明的。如果它的数据类型可排序, 那么也可以指定属性的排序。复合类型本质上和一个表的行类型一样, 但是如果只是想定义一个类型,那么使用CREATE TYPE 就可以避免直接创建实际的表。一个独立的复合类型是有用的,例如, 做为一个函数的参数或者返回类型。
要想能够创建一个复合类型,必须在所有的属性类型上有USAGE权限。
CREATE TYPE的第二种形式创建一个枚举(enum)类型, 在第 8.7 节中描述。枚举类型接受一个或更多的引用标签的列表, 每个标签必须小于NAMEDATALEN字节长度 (在标准的PostgreSQL建立中是64字节)。
CREATE TYPE的第三种形式创建一个新的范围类型, 在第 8.17 节中描述。
范围类型的subtype 可以是有一个相关的b-tree操作符类(决定范围类型值的顺序)的任意类型。 通常子类型的缺省b-tree操作符类用于决定顺序;要使用一个非缺省的操作符类, 用subtype_opclass指定它的名字。 如果子类型是可排序的,并且你希望在范围的排序中使用非缺省的排序, 那么使用collation 选项指定想要的排序。
可选的canonical 函数必须接受一个被定义的范围类型的参数,并且返回相同类型的值。 适用时,这用于转换范围类型到标准形式。参阅第 8.17.8 节 获取更多信息。创建一个canonical 函数比较棘手,因为它必须在范围类型可以被声明之前定义。要做到这点, 必须先创建一个壳类型,壳类型是一个除了名字和所有者之外没有其他属性的占位符类型。 这可以通过发出没有其他额外参数的CREATE TYPE name命令来完成。 然后可以使用该壳类型作为参数和结果声明该函数,最后可以使用相同的名字声明范围类型。 这将自动使用有效的范围类型替代壳类型条目。
可选的subtype_diff函数必须接受两个 subtype类型的值作为参数, 并且返回双精度值表示两个给定值之间的不同。 虽然这是可选的,但是提供它允许GiST索引在范围类型字段上有更大的效率。 参阅第 8.17.8 节获取更多信息。
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 值调用。
这个时候你应该觉得奇怪,输入和输出函数怎么可以声明为返回新类型的结果或者是接受新类型的参数, 而且是在新类型创建之前就需要创建它们。答案是类型必须被首先定义为一个壳类型, 它只是一个除了名称和属主之外没有其他属性的占位符类型。这可以通过没有其他额外参数的 CREATE TYPE name命令来完成。 然后就可以引用该壳类型定义输入输出函数。最后,CREATE TYPE 把这个壳类型替换成完整的、有效的类型定义,这样就可以使用新类型了。
如果支持类型修饰符,那么可选的 type_modifier_input_function 和type_modifier_output_function 是需要的,这是附属于类型声明的可选的约束,如char(5)和 numeric(30,2)。PostgreSQL 允许用户定义的类型接受一个或多个简单的约束或标识符作为修饰符。不过, 这个信息必须能够装进一个非负的整型值里,以在系统表中存储。 type_modifier_input_function 以cstring数组的形式传送声明的修饰符。它必须检查值的有效性 (如果值是错误的则抛出一个错误),如果是正确的,则返回一个非负的 integer值,该值将被作为字段"typmod"存储。如果类型没有 type_modifier_input_function, 那么类型修饰符将被拒绝。type_modifier_output_function 转换内部的整数typmod值为用户显示的正确形式。它必须返回一个cstring 值,该值是附加到类型名之后的准确的字符串;如numeric的函数返回(30,2)。 允许省略type_modifier_output_function, 省略的情况下,缺省显示形式是只有存储的typmod整型值包含在圆括号中。
可选的analyze_function 为该数据类型的字段执行与该类型相关的统计信息收集。缺省时, 如果该类型有个缺省的 B-tree 操作符类,那么ANALYZE 将尝试使用该类型的"等于"和"小于"操作符收集信息。 对于非标量类型,这种行为很可能不合适,因此可以通过提供一个自定义的分析函数覆盖它。 分析函数必须声明为接收单独一个internal类型的参数, 并且返回一个boolean结果。分析函数的详细 API 在 src/include/commands/vacuum.h里。
尽管新类型的内部表现形式只有输入输出函数和其它你创建来使用该类型的函数了解, 但内部表现形式还是有几个属性必须为PostgreSQL声明。 internallength是最重要的一个。 基本数据类型可定义成为定长,这时internallength 是一个正整数,也可以是变长的,通过把internallength 设为VARIABLE表示。(在内部,这个状态是通过将typlen 设置为-1 实现的。)所有变长类型的内部形式都必须以一个四字节整数开头, 这个整数给出此类型这个数值的全长。
可选的标记PASSEDBYVALUE表明该类型的数值是按值而不是引用传递。 你不能传递那些内部形式大于Datum类型尺寸(大多数机器上是 4 字节, 有些是 8 字节)的数据类型的值。
alignment参数声明该数据类型要求的对齐存储方式。 允许的数值等效于按照 1, 2, 4, 8 字节边界对齐。请注意变长类型必须有至少 4 字节的对齐, 因为它们必须包含一个int4作为第一个部分。
storage 参数允许为变长数据类型选择存储策略(定长类型只允许使用plain)。 plain声明该数据类型总是用内联的方式而不是压缩的方式存储。 extended声明系统将首先试图压缩一个长的数据值, 然后如果它仍然太长的话就将它的值移出主表,但系统将不会压缩它。 external声明禁止系统进行压缩并且允许将它的值移出主表。 main允许压缩,但是不赞成把数值移动出主表(如果实在不能放在一行里的话, 仍将移动出主表,它比extended和external项更愿意保存在主表里)。
like_type 参数为指定数据类型的基本代表属性提供一个可选的方式:从某些现有的类型中拷贝他们。 internallength、 passedbyvalue、 alignment和 storage的值从命名的类型中拷贝。 (这是可能的,尽管通常不需要,通过和LIKE子句一起指定他们, 来覆盖一些这些值。)这种方式指定代表,在新类型"piggybacks" 的低级实现以某种方式在现有类型上时尤其有用。
category和 preferred 参数可以用于帮助控制哪个隐式转换将被用于模糊的情况。 每个数据类型都属于一个由单个ASCII字符命名的类,并且每个类型不是"preferred" 就是不在它的类中。当这个规则对于解析重载函数或操作符有帮助时, 解析器更愿意转换为优先的类型(但是只限于来自不在相同类中的其他类型)。 更多信息请参阅第 10 章。对于没有隐式转换到或来自任意其他类型的类型, 保留这些设置作为缺省就足够了。不过,对于有隐式转换的的相关类型的组, 通常标记他们都属于一个类并且在该类中选取一个或两个"最一般" 的类型作为优先是有帮助的。当添加一个用户定义的类型到一个现有的内建类时, category参数尤其有用, 如数值型或字符串类型。但是,也有可能创建新的完全用户定义的类型类别。 选取任意ASCII字符而不是一个大写的字母来命名这样一个类别。
如果用户希望字段的数据类型缺省时不是 NULL ,那么可以在DEFAULT 关键字里声明一个缺省值(可以被附着在特定字段上的DEFAULT子句覆盖)。
用ELEMENT关键字声明数组元素的类型。 比如,ELEMENT = int4定义了一个 4 字节整数 (int4)的数组。有关数组类型的更多细节在下面描述。
可用delimiter 指定用于这种类型数组的外部形式的数值之间的分隔符。 缺省的分隔符是逗号(,)。 请注意分隔符是和数组元素类型相关联,而不是数组类型本身。
如果可选的布尔参数collatable为真, 那么该类型的字段定义和表达式可以通过使用COLLATE 子句携带排序信息。取决于在该类型上函数操作符的实现,以实际上利用排序信息; 仅仅通过标记该类型可排序,这就不会自动发生。
在创建用户定义类型的时候,PostgreSQL 自动创建一个与之关联的数组类型,其名字由该元素类型的名字前缀一个下划线组成, 并且如果有必要保持它小于NAMEDATALEN字节长度时截断。 (如果这样生成的名字与一个现有的类型名冲突,那么重复该进程, 知道找到一个不冲突的名字。)这个隐含创建的数组类型是变长并且使用内建的 array_in和array_out输入和输出函数。 数组类型追踪它的元素类型的所有者或模式的任意更改, 并且如果元素类型有更改时删除。
你很可能会问如果系统自动制作正确的数组类型,那为什么还要有个ELEMENT选项? 使用ELEMENT的场合有一:你定义的定长类型碰巧在内部是一个一定数目相同事物的数组, 而你又想允许这 N 个事物可以通过下标直接访问,除了某些操作符将把该类型当做整体进行处理。 比如,类型point表示为两个浮点数,每个可以用point[0] 和point[1]访问。请注意这个功能只适用于定长类型, 并且其内部形式是一个相同定长域的序列。一个可以下标化的变长类型必须有被 array_in和array_out使用的一般化的内部表现形式。 出于历史原因(也就是,这是明显错误的,但是要改变它却太晚了), 定长数组类型的下标从 0 开始,而不是像变长数组那样的从 1 开始。
将要创建的类型名(可以有模式修饰)
复合类型的一个属性(字段)的名字
要成为一个复合类型的字段的现有数据类型的名字
与复合类型或范围类型字段有关的现有排序的名字
表示与枚举类型的值有关的文本标签的字符串字面值
元素类型的名字,范围类型将代表的范围
该子类型的b-tree操作符类的名字
范围类型的标准化函数的名字
子类型的差异函数的名字
一个函数的名称,将数据从外部文本形式转换成内部格式。
一个函数的名称,将数据从内部格式转换成适于显示的外部文本形式。
一个函数的名称,把数据从类型的外部二进制形式转换成内部形式
一个函数的名称,把数据从类型的内部形式转换成外部二进制形式
一个函数的名称,把修饰符的数组类型转换成内部形式
一个函数的名称,把类型的修饰符的内部形式转换成外部形式
为该数据类型执行统计分析的函数名
一个数值常量,说明新类型的内部表现形式的字节长度。缺省假定它是变长的。
该数据类型的存储对齐要求。如果声明了,必须是char、 int2、int4(缺省)、double之一。
该数据类型的存储策略。如果声明了,必须是plain(缺省)、 external、extended、main之一。
与新类型将要有的表现相同的现有数据类型的名字。 internallength、 passedbyvalue、 alignment、 storage的值是从该类型中拷贝的, 除非在CREATE TYPE命令中明确的说明覆写。
这个类型的类别代码(单个ASCII字符)。"用户定义类型"却省是'U'。 其他标准类别代码可以在表 48-53中找到。 你也可以选择其他ASCII字符来创建自定义类别。
如果这个类型在它的类型类别中是首选类型则为真,否则为假。缺省是假。 在一个现有的类型类别中创建一个新的首选类型时要非常小心, 因为这会导致行为上意外的变化。
该类型的缺省值。若省略则为 NULL
被创建的类型是数组;这个声明数组元素的类型。
数组元素之间分隔符
如果这个类型的操作可以使用排序信息则为真。缺省为假。
因为一旦类型被创建之后对它的使用就没有限制, 所以创建一个基本类型或范围类型就等价于授予所有用户执行类型定义中指定的各个函数的权限, 这对于大多数类型定义中指定的函数来说不会造成什么不良问题。 但是如果你设计的新类型在内部形式和外部形式之间转换的时候使用"敏感信息", 那么你仍然要再三考虑、多加小心。
PostgreSQL版本8.3之前, 生成的数组类型的名字总是正好是元素类型的名字前置一个下划线字符(_)。 (因此限制类型名字的长度比其他名字的字符要少。)虽然这仍然是通常的情况, 但是数组类型名字可能会有变化,假使最大长度名字或与下划线开始的用户类型名字冲突。 依赖于这个约定的书写代码因此弃用了。取而代之,使用pg_type.typarray 来定位与一个给定类型相关的数组类型。
避免使用以下划线开始的类型和表名是明智的。虽然服务器将改变生成的数组类型名, 以避免与用户给定的名字冲突,但是仍然有混淆的更显, 尤其是老的客户端软件可能假设以下划线开始的类型名总是代表数组。
PostgreSQL版本8.2之前,壳类型创建语法 CREATE TYPE name并不存在。 创建新的基本类型之前必须首先创建其输入函数。这样,PostgreSQL 将会首先把新类型的名字看作输入函数的返回类型并隐含创建壳类型, 然后这个壳类型将被随后定义的输入输出函数引用。这种老式的方法目前仍然被支持, 但已经反对使用,将来可能不再支持。同样,为了避免函数定义中的临时壳类型偶然地搞乱系统表, 当输入函数用 C 语言书写时,将只能用这种方法创建壳类型。
在PostgreSQL 7.3 以前,要通过使用占位伪类型 opaque代替函数的前向引用来避免创建壳类型。7.3 之前cstring 参数和结果同样需要声明为opaque。要支持加载旧的转储文件, CREATE TYPE将接受那些用opaque声明的输入输出函数, 但是它将发出一条通知并且用正确的类型改变函数的声明。
这个例子创建一个复合类型并且在一个函数定义中使用它:
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 );
更多的例子,包括合适的输入和输出函数,位于第 35.11 节。