CREATE TYPE
CREATE TYPE — 定义一种新的数据类型
大纲
CREATE TYPEnameAS (attribute_namedata_type[, ... ] ) CREATE TYPEname( INPUT =input_function, OUTPUT =output_function[ , RECEIVE =receive_function] [ , SEND =send_function] [ , INTERNALLENGTH = {internallength| VARIABLE } ] [ , PASSEDBYVALUE ] [ , ALIGNMENT =alignment] [ , STORAGE =storage] [ , DEFAULT =default] [ , ELEMENT =element] [ , DELIMITER =delimiter] )
描述
CREATE TYPE 为当前数据库注册一种新的数据类型。定义类型的用户将成为其所有者。
如果给出了模式名,则该类型会在指定模式中创建;否则会在当前模式中创建。类型名必须不同于同一模式中任何现有类型或域的名称。(由于表也具有关联的数据类型,类型名还必须不同于同一模式中任何现有表的名称。)
复合类型
CREATE TYPE 的第一种形式创建一个复合类型。复合类型由属性名称和数据类型的列表指定。它本质上与表的行类型相同,但当只想定义一个类型时,使用
CREATE TYPE 可以避免创建实际的表。独立的复合类型适合作为函数的返回类型。
基本类型
CREATE TYPE 的第二种形式创建新的基本类型(标量类型)。参数可以按任意顺序出现,不必是上面展示的顺序,而且大多数是可选的。在定义类型之前,你必须(用
CREATE FUNCTION)先注册两个或更多函数。支持函数
input_function 和
output_function
是必需的,而函数
receive_function 和
send_function
是可选的。一般来说,这些函数必须用
C 或其他低级语言编写。
input_function
把该类型的外部文本表示转换为为该类型定义的操作符和函数所使用的内部表示。output_function
执行反向的转换。输入函数可以声明为接受一个
cstring 类型的参数,或者接受
cstring、oid、integer
三个类型的参数。第一个参数是作为
C 字符串的输入文本,第二个参数是数组类型情况下的元素类型,第三个是目标列的
typmod(如果已知)。输入函数应当返回该数据类型本身的值。输出函数可以声明为接受一个新数据类型的参数,或者接受两个参数且第二个参数为
oid 类型。第二个参数同样是数组类型时的数组元素类型。输出函数应当返回
cstring 类型。
可选的
receive_function
把该类型的外部二进制表示转换为内部表示。如果不提供该函数,该类型就无法参与二进制输入。二进制表示应当选择转换成内部形式代价低廉、同时具有合理可移植性的形式。(例如,标准整数数据类型使用网络字节序作为外部二进制表示,而内部表示是机器的本机字节序。)接收函数应当执行充分的检查以确保值是有效的。接收函数可以声明为接受一个
internal 类型的参数,或者
internal 和
oid 两个类型的参数。它必须返回该数据类型本身的值。(第一个参数是指向持有收到的字节串的
StringInfo 缓冲区的指针;可选的第二个参数是数组类型情况下的元素类型。)类似地,可选的
send_function 把内部表示转换为外部二进制表示。如果不提供该函数,该类型就无法参与二进制输出。发送函数可以声明为接受一个新数据类型的参数,或者接受两个参数且第二个参数为
oid 类型。第二个参数同样是数组类型时的数组元素类型。发送函数必须返回
bytea 类型。
读到这里,你可能会问:既然新类型本身还没创建,输入和输出函数怎么能声明为返回或接受这个新类型呢?答案是,必须先创建输入函数,然后是输出函数(以及需要的二进制 I/O 函数),最后才是数据类型本身。PostgreSQL会首先把新数据类型的名称看作输入函数的返回类型。此时它会创建一个“shell”类型,它只是系统目录中的一个占位条目,并把输入函数的定义链接到该 shell
类型。类似地,其他函数也会链接到(此时已经存在的)shell 类型。最后,CREATE TYPE用完整的类型定义替换该 shell
条目,此后新类型就可以使用了。
虽然新类型内部表示的细节只有 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
都会自动创建一个关联的数组类型,其名称由基础类型名前加一个下划线组成。解析器理解这一命名约定,会把对foo[]类型列的请求转换为对_foo类型的请求。这种隐式创建的数组类型是变长的,并使用内置的输入/输出函数
array_in和array_out。
如果系统会自动创建正确的数组类型,你可能会合理地问,为什么还需要ELEMENT选项。ELEMENT唯一有用的情况是:你正在创建一种定长类型,而它在内部恰好是若干相同元素组成的数组,并且除了为整个类型提供的操作之外,你还希望允许通过下标直接访问这些元素。例如,类型name就允许用这种方式访问其组成元素char。一个二维的
point类型可以允许像point[0]和
point[1]这样访问它的两个分量数字。注意,这种机制只适用于内部形式恰好是一串相同定长字段的定长类型。可用下标访问的变长类型必须采用array_in和array_out使用的通用内部表示。由于历史原因(也就是说这显然不对,但现在改已经太晚了),定长数组类型的下标从零开始,而变长数组则从一开始。
参数
name要创建的类型名称(可选地带有模式限定)。
attribute_name复合类型的一个属性(列)的名称。
data_type将成为复合类型一列的现有数据类型名称。
input_function将数据从类型的外部文本形式转换为内部形式的函数名。
output_function将数据从类型的内部形式转换为外部文本形式的函数名。
receive_function将数据从类型的外部二进制形式转换成内部形式的函数名。
send_function将数据从类型的内部形式转换为外部二进制形式的函数名。
internallength一个数字常量,用于指定新类型内部表示的字节长度。默认假定它是变长的。
alignment该数据类型的存储对齐需求。如果被指定,它必须是
char、int2、int4或者double。默认是int4。storage该数据类型的存储策略。如果被指定,必须是
plain、external、extended或者main。默认是plain。default该数据类型的默认值。若省略,默认值为 null。
element正在创建的类型是数组;该参数指定数组元素类型。
delimiter在由该类型构成的数组中各值之间使用的分隔符字符。
注意
用户定义的类型名不能以下划线字符(_)开头,并且长度最多只能是 62 个字符(通常来说是
NAMEDATALEN - 2,而不是其他名称允许的
NAMEDATALEN - 1 个字符)。以下划线开头的类型名保留给内部创建的数组类型名使用。
在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;
这个示例创建基础类型box,然后在表定义中使用它:
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
);
更多例子(包括合适的输入和输出函数)见第 33 章。
兼容性
这个
CREATE TYPE 命令是
PostgreSQL 的扩展。SQL99 中也有一个
CREATE TYPE 语句,但在细节上大不相同。