CREATE TYPE
CREATE TYPE — 定义一种新的数据类型
大纲
CREATE TYPEtypename( INPUT =input_function, OUTPUT =output_function, INTERNALLENGTH = {internallength| VARIABLE } [ , DEFAULT =default] [ , ELEMENT =element] [ , DELIMITER =delimiter] [ , PASSEDBYVALUE ] [ , ALIGNMENT =alignment] [ , STORAGE =storage] ) CREATE TYPEtypenameAS (column_namedata_type[, ... ] )
输入
typename要创建的类型名称(可选地带有模式限定)。
internallength一个字面值,指定新类型的内部长度。
input_function一个由
CREATE FUNCTION创建的函数的名称,它把数据从外部形式转换为该类型的内部形式。output_function一个由
CREATE FUNCTION创建的函数的名称,它把数据从内部形式转换为适合显示的形式。element正在创建的类型是数组;该参数指定数组元素类型。
delimiter在由该类型构成的数组中各值之间使用的分隔符字符。
default该数据类型的默认值。通常省略此项,使默认值为 NULL。
alignment该数据类型的存储对齐要求。如果指定,必须是
char、int2、int4或double;默认为int4。storage该数据类型的存储技术。如果指定,必须是
plain、external、extended或main;默认为plain。column_name复合类型的列名。
data_type现有数据类型的名称。
输出
CREATE TYPE类型创建成功时返回的消息。
描述
CREATE TYPE 允许用户向
PostgreSQL 注册一种新的数据类型,供当前数据库使用。定义类型的用户将成为其所有者。
如果给出了模式名称,那么类型会创建在指定的模式中。否则它创建在当前模式中(即搜索路径最前面的那个;见
CURRENT_SCHEMA())。类型名必须与同一模式中任何现有类型或域的名称不同。(由于表都有关联的数据类型,类型名也不得与同一模式中的表名冲突。)
基本类型
CREATE TYPE 的第一种形式创建新的基本类型(标量类型)。它要求在定义类型之前(用
CREATE FUNCTION)先注册两个函数。新基本类型的表示由
input_function 决定,它把该类型的外部表示转换为为该类型定义的操作符和函数可用的内部表示。自然,output_function
执行反向的转换。输入函数可以声明为接受一个
cstring 类型的参数,或者接受
cstring、OID、int4
三个类型的参数。(第一个参数是作为
C 字符串的输入文本,第二个参数是数组类型情况下的元素类型,第三个是目标列的
typmod(如果已知)。)它应当返回该数据类型本身的值。输出函数可以声明为接受一个新数据类型的参数,或者接受两个参数且第二个参数为
OID 类型。(第二个参数同样是数组类型时的数组元素类型。)输出函数应当返回
cstring 类型。
读到这里,你可能会问:既然新类型本身还没创建,输入和输出函数怎么能声明为返回或接受这个新类型呢?答案是,必须先创建输入函数,然后是输出函数,最后才是数据类型。PostgreSQL 最初会作为输入函数的返回类型看到新数据类型的名字。它会创建一个“壳”类型——这只是
pg_type 中的一个占位条目——并把输入函数定义链接到该壳类型。类似地,输出函数会链接到(此时已存在的)壳类型。最后,CREATE TYPE 用完整的类型定义替换壳条目,新类型就可以使用了。
注意
在 7.3 之前的
PostgreSQL 版本中,习惯的做法是把函数对类型名的前向引用替换为占位伪类型
OPAQUE 来避免创建壳类型。cstring
的输入和结果在
7.3 之前也不得不声明为
OPAQUE。为了支持装载旧的转储文件,CREATE TYPE 会接受用
opaque 声明的函数,但它会发出一条
NOTICE,并把函数的声明改为使用正确的类型。
新的基本数据类型可以是定长的,这时
internallength
是一个正整数;也可以是变长的,通过把
internallength
设为 VARIABLE 来表示。(在内部,这通过把
typlen 设为
-1 来表示。)所有变长类型的内部表示都必须以一个给出该类型该值总长度的整数开头。
要指明一个类型是数组,可以用
ELEMENT
关键字指定数组元素的类型。例如,要定义一个
4 字节整数("int4")的数组,指定
ELEMENT = int4
关于数组类型的更多细节见下文。
要指明该类型数组的外部表示中值与值之间使用的分隔符,可以把
delimiter
设为某个特定的字符。默认分隔符是逗号(',')。注意,分隔符与数组元素类型相关联,而不是与数组类型本身。
如果用户希望该数据类型的列默认为
NULL 以外的值,可以指定一个默认值。用
DEFAULT
关键字指定默认值。(这样的默认值可以被附加在特定列上的显式
DEFAULT
子句覆盖。)
可选标志
PASSEDBYVALUE 表示该数据类型的值按值传递而不是按引用传递。注意,内部表示比
Datum 类型宽度(多数机器上
4 字节,少数机器上
8 字节)更长的类型不能按值传递。
alignment
关键字指定该数据类型所需的存储对齐。允许的取值分别相当于按
1、2、4 或
8 字节边界对齐。注意,变长类型的对齐至少必须是
4,因为它们必然以一个
int4 作为第一个成分。
storage
关键字允许为变长数据类型选择存储策略(定长类型只允许
plain)。plain 对该数据类型禁用
TOAST:它总是内联存储且不压缩。extended 提供完整的
TOAST 能力:系统会先尝试压缩较长的数据值,如果仍然太长,就把该值移出主表行。external 允许把值移出主表,但系统不会尝试压缩它。main 允许压缩,但不鼓励把值移出主表。(采用这种存储方法的数据项在实在没有其他办法让行放得下时仍可能被移出主表,但相比
extended 和
external 的数据项,它们会被优先保留在主表中。)
复合类型
CREATE TYPE 的第二种形式创建一个复合类型。复合类型由列名和数据类型的列表指定。它本质上与表的行类型相同,但当只想定义一个类型时,使用
CREATE TYPE 可以避免创建实际的表。独立的复合类型适合作为函数的返回类型。
数组类型
每当创建用户定义的基础数据类型时,PostgreSQL
都会自动创建一个关联的数组类型,其名称由基础类型名前加一个下划线组成。解析器理解这一命名约定,会把对foo[]类型列的请求转换为对_foo类型的请求。这种隐式创建的数组类型是变长的,并使用内置的输入/输出函数
array_in和array_out。
如果系统会自动创建正确的数组类型,你可能会合理地问“为什么还需要
ELEMENT
选项?”ELEMENT唯一有用的情况是:你正在创建一种定长类型,而它在内部恰好是
N
个相同元素组成的数组,并且除了为整个类型计划提供的操作之外,你还希望允许通过下标直接访问这
N 个元素。例如,类型name就允许用这种方式访问其组成元素char。一个二维的
point类型可以允许像point[0]和point[1]这样访问它的两个分量浮点数。注意,这一机制只对内部形式恰好是
N
个相同定长字段序列的定长类型有效。可下标访问的变长类型必须使用
array_in和array_out所使用的广义内部表示。由于历史原因(也就是说,这显然是错的,但要改已经太迟了),定长数组类型的下标从零开始,而不像变长数组那样从一开始。
注意
用户定义的类型名不能以下划线字符(“_”)开头,且长度只能是
62
个字符(或者一般来说是
NAMEDATALEN-2,而不是其他名称允许的
NAMEDATALEN-1
个字符)。以下划线开头的类型名保留给内部创建的数组类型名使用。
示例
这个示例创建 box 数据类型,然后在表定义中使用该类型:
CREATE TYPE box (INTERNALLENGTH = 16,
INPUT = my_procedure_1, OUTPUT = my_procedure_2);
CREATE TABLE myboxes (id INT4, description box);
如果 box 的内部结构是四个
float4 组成的数组,我们也可以改成这样写:
CREATE TYPE box (INTERNALLENGTH = 16,
INPUT = my_procedure_1, OUTPUT = my_procedure_2,
ELEMENT = float4);这样就允许通过下标访问 box 值的各分量浮点数。除此之外该类型的行为与之前完全相同。
这个示例创建一种大对象类型,并在表定义中使用它:
CREATE TYPE bigobj (INPUT = lo_filein, OUTPUT = lo_fileout,
INTERNALLENGTH = VARIABLE);
CREATE TABLE big_objs (id int4, obj bigobj);
这个示例创建一个复合类型,并在表函数定义中使用它:
CREATE TYPE compfoo AS (f1 int, f2 text); CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS 'SELECT fooid, fooname FROM foo' LANGUAGE SQL;
兼容性
这个
CREATE TYPE 命令是
PostgreSQL 的扩展。SQL99 中也有一个
CREATE TYPE 语句,但在细节上大不相同。