CREATE TYPE
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 ]
)
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注册一个新数据类型,以便在当前数据库中使用。定义类型的用户将成为其所有者。
如果给出了模式名称,则在指定的模式中创建类型。否则,它将在当前架构中创建。类型名称必须与同一模式中任何现有类型或域的名称不同。 (因为表具有关联的数据类型,所以类型名称也必须与同一模式中任何现有表的名称不同。)
CREATE TYPE有五种形式,如上面的语法语法格式所示。它们分别创建复合类型,枚举类型,范围类型,基本类型或shell类型。其中前四个将在下面依次讨论。 shell类型只是一个占位符,用于稍后定义的类型;它是通过发出没有参数说明的CREATE TYPE创建的,除了类型名称。在创建范围类型和基类型时,需要使用Shell类型作为前向引用,如这些部分中所述。
复合类型
CREATE TYPE的第一种形式创建了一种复合类型。复合类型由属性名称和数据类型列表指定。如果属性的归类可以归类,则也可以指定属性的归类。复合类型与表的行类型基本相同,但使用CREATE TYPE时,无需创建实际表,只需要定义类型即可。一个独立的复合类型很有用,例如,作为函数的参数说明或返回类型。
要能够创建复合类型,您必须对所有属性类型具有 USAGE 权限。
枚举类型
CREATE TYPE的第二种形式创建枚举(枚举)类型。枚举类型采用带引号的标签列表,每个标签的长度必须小于NAMEDATALEN 字节(标准Vastbase E100构建中为64字节)。(可以创建一个带有零标签的枚举类型,但在使用 ALTER TYPE添加至少一个标签之前,不能使用这种类型来保存值。)
范围类型
CREATE TYPE 的第三种形式创建了一个新的范围类型。
范围类型的subtype可以 是任何带有一个相关的 B 树操作符类(用来决定该范围类型值的顺序)的类型。 通常,子类型的默认 B 树操作符类被用来决定顺序。要使用一种非默认操作符 类,可以用 subtype_opclass指定它 的名字。如果子类型是可排序的并且希望在该范围的顺序中使用一种非默认的 排序规则,可以用collation选项来指定。
可选的canonical函数 必须接受一个所定义的范围类型的参数,并且返回同样类型的一个值。在适 用时,它被用来把范围值转换成一种规范的形式。创建一个 canonical函数有点棘 手,因为必须在声明范围类型之前定义它。要这样做,必须首先创建一种 shell 类型,它是一种没有属性只有名称和拥有者的占位符类型。这可以通过 发出不带额外参数的命令CREATE TYPE name来完成。然后可以使用该 shell 类型作为 参数和结果来声明该函数,并且最终用同样的名称来声明范围类型。这会自动 用一种合法的范围类型替换 shell 类型项。
可选的subtype_diff函数 必须接受两个subtype类型 的值作为参数,并且返回一个double precision值表示两个给定 值之间的差别。虽然这是可选的,但是提供这个函数会让该范围类型列上 GiST 索 引效率更高。
基本类型
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 值不会调用输出函数。
T可选的receive_function 会把类型的外部二进制表达转换成内部表达。如果没有提供这个函数, 该类型不能参与到二进制输入中。二进制表达转换成内部形式代价更低, 然而却更容易移植(例如,标准的整数数据类型使用网络字节序作为外 部二进制表达,而内部表达是机器本地的字节序)。接收函数应该执行 足够的检查以确保该值是有效的。接收函数可以被声明为有一个 internal类型的参数,或者有三个类型分别为 internal、oid、integer 的参数。第一个参数是一个指向StringInfo缓冲区的 指针,其中保存着接收到的字节串。其余可选的参数和文本输入函数的 相同。接收函数必须返回一个该数据类型本身的值。通常,一个接收函 数应该被声明为 STRICT。如果不是这样,在读到一个 NULL 输入值时, 调用它时第一个参数会是 NULL。在这种情况下,该函数必须仍然返回 NULL,除非它发生了错误(这种情况主要是想支持域接收函数,它们 可能需要拒绝 NULL 输入)。类似地,可选的 send_function将 内部表达转换成外部二进制表达。如果没有提供这个函数,该类型将不 能参与到二进制输出中。发送函数必须被声明为有一个新数据类型的参 数。发送函数必须返回类型bytea。对于 NULL 值不 会调用发送函数。
到这里你应该在疑惑输入和输出函数是如何能被声明为具有新类型的结果或参数的?因为必须在创建新类型之前创建这两个函数。这个问题 的答案是,新类型应该首先被定义为一种shell type, 它是一种占位符类型,除了名称和拥有者之外它没有其他属性。这可以 通过不带额外参数的命令CREATE TYPE name做到。然后用 C 写的 I/O 函数可以 被定义为引用这种 shell type。最后,用带有完整定义的 CREATE TYPE把该 shell type 替换为一个完全的、合 法的类型定义,之后新类型就可以正常使用了。
如果该类型支持修饰符(附加在类型声明上的可选约束,例如 char(5)或者numeric(30,2)),则需要可选的 type_modifier_input_function 以及type_modifier_output_function。 Vastbase E100允许用户定义的类型有一个或者 多个简单常量或者标识符作为修饰符。不过,为了存储在系统目录中,该信息必须 能被打包到一个非负整数值中。所声明的修饰符会被以cstring数组的形式 传递给 type_modifier_input_function。 它必须检查该值的合法性(如果值错误就抛出一个错误),如果值正确,要返回 一个非负integer值,它将被存储在“typmod”列中。如果 类型没有 type_modifier_input_function 则类型修饰符将被拒绝。 type_modifier_output_function 把内部的整数 typmod 值转换回正确的形式用于用户显示。它必须返回一个 cstring值,该值就是追加到类型名称后的字符串。例如 numeric的函数可能会返回(30,2)。如果默认的显示格式 就是只把存储的 typmod 整数值放在圆括号内,则允许省略 type_modifier_output_function。
可选的analyze_function 为该数据类型的列执行与类型相关的统计信息收集。默认情况下,如果 该类型有一个默认的 B-树操作符类,ANALYZE将尝试用 类型的“equals”和“less-than”操作符来收集统计信息。 这种行为对于非标量类型并不合适,因此可以通过指定一个自定义分析函数来 覆盖这种行为。分析函数必须被声明为有一个类型为internal的参 数,并且返回一个boolean结果。分析函数的详细 API 请见 src/include/commands/vacuum.h。
虽然只有 I/O 函数和其他为该类型创建的函数才知道新类型的内部表达的细节, 但是内部表达的一些属性必须被向Vastbase E100声明。其中最重要的是 internallength。基本数据 类型可以是定长的(这种情况下 internallength是一个正 整数)或者是变长的(把 internallength设置为 VARIABLE,在内部通过把typlen设置为 -1 表示)。 所有变长类型的内部表达都必须以一个 4 字节整数开始,它给出了这个值的总 长度(注意如第 68.2 节中所述,长度域常常是被编码 过的,直接接受它是不明智的)。
可选的标志PASSEDBYVALUE表示这种数据类型的值需要 被传值而不是传引用。传值的类型必须是定长的,并且它们的内部表达不能超 过Datum类型(某些机器上是 4 字节,其他机器上是 8 字节)的 尺寸。
alignment参数指定数据 类型的存储对齐要求。允许的值等同于以 1、2、4 或 8 字节边界对齐。注意 变长类型的 alignment 参数必须至少为 4,因为它们需要包含一个 int4作为它们的第一个组成部分。
storage参数允许 为变长数据类型选择存储策略(对定长类型只允许 plain)。plain指定该类型的数 据将总是被存储在线内并且不会被压缩。extended 指定系统将首先尝试压缩一个长的数据值,并且将在数据仍然太长的情 况下把值移出主表行。external允许值被移出主表, 但是系统将不会尝试对它进行压缩。main允许压缩, 但是不鼓励把值移出主表(如果没有其他办法让行的大小变得合适,具有 这种存储策略的数据项仍将被移出主表,但比起 extended以及external项来, 这种存储策略的数据项会被优先考虑保留在主表中)。
除plain之外所有的 storage值都暗示 该数据类型的函数能处理被TOAST 过的值。指定的值 仅仅是决定一种可 TOAST 数据类型的列的默认 TOAST 存储策略,用户 可以使用ALTER TABLE SET STORAGE为列选取其他策略。
like_type参数提供 了另一种方法来指定一种数据类型的基本表达属性:从某种现有的类型中 拷贝。internallength、 passedbyvalue、 alignment和 storage的值会从指 定的类型中复制而来(也可以通过在LIKE子句中指定这些属 性的值来覆盖复制过来的值,不过通常并不这么做)。当新类型的低层 实现是以一种现有的类型为“载体”时,用这种方式指定表达特别有用。
category和 preferred参数可以被用来 帮助控制在混淆的情况下应用哪一种隐式造型。每一种数据类型都属于一个用 单个 ASCII 字符命名的分类,并且每一种类型可以是其所属分类中的 “首选”。当有助于解决重载函数或操作符时,解析器将优先 造型到首选类型(但是只能从同类的其他类型造型)。对于没有隐式造型到任意其他类型或者 从任意其他类型造型的类型,让这些设置保持默认即可。不过,对于一组 具有隐式造型的相关类型,把它们都标记为属于同一个类别并且选择一种 或两种“最常用”的类型作为该类别的首选通常是很有用的。在 把一种用户定义的类型增加到一个现有的内建类别(例如数字或者字符串 类型)中时, category参数特别 有用。不过,也可以创建新的全部是用户定义类型的类别。对这样的类别, 可选择除大写字母之外的任何 ASCII 字符。
如果用户希望该数据类型的列被默认为某种非空值,可以指定一个默认值。 默认值可以用DEFAULT关键词指定(这样一个默认值 可以被附加到一个特定列的显式DEFAULT子句覆盖)。
要指定一种类型是数组,用ELEMENT关键词指定该数组元素 的类型。例如,要定义一个 4 字节整数的数组(int4), 应指定ELEMENT = int4。
要指定在这种类型数组的外部表达中分隔值的定界符,可以把delimiter设置为一个特定字符。默认 的定界符是逗号(,)。注意定界符是与数组元素类型相 关的,而不是数组类型本身相关。
如果可选的布尔参数 collatable为真,这种 类型的列定义和表达式可能通过使用COLLATE子句携带 有排序规则信息。在该类型上操作的函数的实现负责真正利用这些信息,仅 把类型标记为可排序的并不会让它们自动地去使用这类信息。
数组类型
每当创建用户定义的类型时,Vastbase E100会自动创建一个关联的数组类型,其名称由前缀为下划线的元素类型名称组成,并在必要时截断,以使其长度小于 NAMEDATALEN 个字节。 (如果生成的名称与现有类型名称冲突,则重复该过程,直到找到非冲突名称。)此隐式创建的数组类型是可变长度,并使用内置输入和输出函数 array_in 和 array_out 。数组类型跟踪其元素类型的所有者或模式中的任何更改,如果元素类型为,则将其删除。
如果系统自动生成正确的数组类型,您可能会合理地问为什么有 ELEMENT 选项。使用 ELEMENT 唯一有用的情况是,当你制作一个固定长度的类型,恰好在内部是一系列相同的东西,并且你想允许通过下标直接访问这些东西,除了您计划为整个类型提供的任何操作。例如,类型 point 仅表示为两个浮点数,可以使用 point[0] 和 point[1] 进行访问。请注意,此工具仅适用于固定长度类型,其内部形式恰好是一系列相同的固定长度字段。可订阅的变长类型必须具有 array_in 和 array_out 使用的通用内部表示。由于历史原因(即,这显然是错误的,但改变它已经太迟了),固定长度数组类型的下标从零开始,而不是从可变长度数组开始。
参数说明
name
要创建的类型的名称(可以被模式限定)。
attribute_name
组合类型的一个属性(列)的名称。
data_type
要成为组合类型的一个列的现有数据类型的名称。
collation
要关联到组合类型的一列或者范围类型的现有排序规则的名称。
label
一个字符串,它表达与枚举类型的一个值相关的文本标签。
subtype
范围类型的元素类型的名称,范围类型表示的范围属于该类型。
subtype_operator_class
用于 subtype 的 B 树操作符类的名称。
canonical_function
范围类型的规范化函数的名称。
subtype_diff_function
用于 subtype 的差函数的名称。
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。
like_type
与新类型具有相同表达的现有数据类型的名称。会从这个类型中复制 internallength、 passedbyvalue、 alignment以及 storage的值( 除非在这个CREATE TYPE命令的其他地方用显式说 明覆盖)。
category
这种类型的分类码(一个 ASCII 字符)。 默认是 “用户定义类型”的'U'。其他的标准分类码可见 表 52.63。为了创建自定义分类, 你也可以选择其他 ASCII 字符。
preferred
如果这种类型是其类型分类中的优先类型则为真,否则为假。默认 为假。在一个现有类型分类中创建一种新的优先类型要非常小心, 因为这可能会导致行为上令人惊奇的改变。
default
数据类型的默认值。如果被省略,默认值是空。
element
被创建的类型是一个数组,这指定了数组元素的类型。
delimiter
在由这种类型组成的数组中值之间的定界符。
collatable
如果这个类型的操作可以使用排序规则信息,则为真。默认为假。
示例
此示例创建一个复合类型并在函数定义中使用它:
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
);
这将允许通过下标访问框值的组件编号。否则,类型的行为与以前相同。
此示例创建一个大对象类型并在表定义中使用它:
CREATE TYPE bigobj (
INPUT = lo_filein, OUTPUT = lo_fileout,
INTERNALLENGTH = VARIABLE
);
CREATE TABLE big_objs (
id integer,
obj bigobj
);