CREATE TYPE
功能描述
在当前数据库中定义一种新的数据类型。定义数据类型的用户将成为该数据类型的拥有者。类型只适用于行存表。
有四种形式的CREATE TYPE,分别为:复合类型、基本类型、shell类型和枚举类型。
注意事项
如果给定一个模式名,那么该类型将被创建在指定的模式中,否则它会被创建在当前模式中。类型名称必须与同一个模式中任何现有的类型或者域有所区别(因为表具有相关的数据类型,类型名称也必须与同一个模式中任何现有表的名字不同)。
语法格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
CREATE TYPE name AS ( [ attribute_name data_type [ COLLATE collation ] [, ... ] ] ) 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 name AS ENUM ( [ 'label' [, ... ] ] ) |
参数说明
复合类型
- name
要创建的类型的名称(可以被模式限定)。
- attribute_name
复合类型的一个属性(列)的名称。
- data_type
要成为复合类型的一个列的现有数据类型的名称。
- collation
要关联到复合类型的一列的现有排序规则的名称。
基本类型
自定义基本类型时,参数可以以任意顺序出现,input_function和output_function为必选参数,其它为可选参数。
- input_function
将数据从类型的外部文本形式转换为内部形式的函数名。
输入函数可以被声明为有一个cstring类型的参数,或者有三个类型分别为cstring、oid、integer的参数。
- cstring参数是以C字符串存在的输入文本。
- oid参数是该类型自身的OID(对于数组类型则是其元素类型的OID)。
- integer参数是目标列的typmod(如果知道,不知道则将传递-1)。
输入函数必须返回一个该数据类型本身的值。通常,一个输入函数应该被声明为STRICT。如果不是这样,在读到一个NULL输入值时,调用输入函数时第一个参数会是NULL。在这种情况下,该函数必须仍然返回NULL,除非调用函数发生了错误(这种情况主要是想支持域输入函数,域输入函数可能需要拒绝NULL输入)。
输入和输出函数能被声明为具有新类型的结果或参数是因为:必须在创建新类型之前创建这两个函数。而新类型应该首先被定义为一种shell type,它是一种占位符类型,除了名称和拥有者之外它没有其他属性。这可以通过不带额外参数的命令CREATE TYPE name做到。然后用C写的I/O函数可以被定义为引用这种shell type。最后,用带有完整定义的CREATE TYPE把该shell type替换为一个完全的、合法的类型定义,之后新类型就可以正常使用了。
- output_function
将数据从类型的内部形式转换为外部文本形式的函数名。
输出函数必须被声明为有一个新数据类型的参数。输出函数必须返回类型cstring。对于NULL值不会调用输出函数。
- receive_function
可选参数。将数据从类型的外部二进制形式转换成内部形式的函数名。
如果没有该函数,该类型不能参与到二进制输入中。二进制表达转换成内部形式代价更低,然而却更容易移植(例如,标准的整数数据类型使用网络字节序作为外部二进制表达,而内部表达是机器本地的字节序)。receive_function应该执行足够的检查以确保该值是有效的。
接收函数可以被声明为有一个internal类型的参数,或者有三个类型分别为internal、oid、integer的参数。
- internal参数是一个指向StringInfo缓冲区的指针,其中保存着接收到的字节串。
- oid和integer参数和文本输入函数的相同。
接收函数必须返回一个该数据类型本身的值。通常,一个接收函数应该被声明为STRICT。如果不是这样,在读到一个NULL输入值时调用接收函数时第一个参数会是NULL。在这种情况下,该函数必须仍然返回NULL,除非接收函数发生了错误(这种情况主要是想支持域接收函数,域接收函数可能需要拒绝NULL输入)。
- send_function
可选参数。将数据从类型的内部形式转换为外部二进制形式的函数名。
如果没有该函数,该类型将不能参与到二进制输出中。发送函数必须被声明为有一个新数据类型的参数。发送函数必须返回类型bytea。对于NULL值不会调用发送函数。
- type_modifier_input_function
可选参数。将类型的修饰符数组转换为内部形式的函数名。
- type_modifier_output_function
可选参数。将类型的修饰符的内部形式转换为外部文本形式的函数名。
如果该类型支持修饰符(附加在类型声明上的可选约束,例如char(5)或numeric(30,2)),则需要可选的type_modifier_input_function以及type_modifier_output_function。GaussDB(DWS)允许用户定义的类型有一个或者多个简单常量或者标识符作为修饰符。不过,为了存储在系统目录中,该信息必须能被打包到一个非负整数值中。所声明的修饰符会被以cstring数组的形式传递给type_modifier_input_function。type_modifier_input_function必须检查该值的合法性(如果值错误就抛出一个错误),如果值正确,要返回一个非负integer值,该值将被存储在“typmod”列中。如果类型没有type_modifier_input_function则类型修饰符将被拒绝。type_modifier_output_function把内部的整数typmod值转换回正确的形式用于用户显示。type_modifier_output_function必须返回一个cstring值,该值就是追加到类型名称后的字符串。例如,numeric的函数可能会返回(30,2)。如果默认的显示格式就是只把存储的typmod整数值放在圆括号内,则允许省略type_modifier_output_function。
- analyze_function
可选参数。为该数据类型执行统计分析的函数名的可选参数。
默认情况下,如果该类型有一个默认的B-tree操作符类,ANALYZE将尝试用类型的“equals”和“less-than”操作符来收集统计信息。这种行为对于非标量类型并不合适,因此可以通过指定一个自定义分析函数来覆盖这种行为。分析函数必须被声明为有一个类型为internal的参数,并且返回一个boolean结果。
- internallength
可选参数。一个数字常量,用于指定新类型的内部表达的字节长度。默认为变长。
虽然只有I/O函数和其他为该类型创建的函数才知道新类型的内部表达的细节,但是内部表达的一些属性必须被向GaussDB(DWS)声明。其中最重要的是internallength。基本数据类型可以是定长的(这种情况下internallength是一个正整数)或者是变长的(把internallength设置为VARIABLE,在内部通过把typlen设置为-1表示)。所有变长类型的内部表达都必须以一个4字节整数开始,internallength定义了总长度。
- PASSEDBYVALUE
可选参数。表示这种数据类型的值需要被传值而不是传引用。传值的类型必须是定长的,并且它们的内部表达不能超过Datum类型(某些机器上是4字节,其他机器上是8字节)的尺寸。
- alignment
可选参数。该参数指定数据类型的存储对齐需求。如果被指定,必须是char、int2、int4或者double。默认是int4。
允许的值等同于以1、2、4或8字节边界对齐。要注意变长类型的alignment参数必须至少为4,因为它们需要包含一个int4作为它们的第一个组成部分。
- storage
可选参数。该数据类型的存储策略。
如果被指定,必须是plain、external、extended或者main。默认是plain。
- plain指定该类型的数据将总是被存储在线内并且不会被压缩。(对定长类型只允许plain)
- extended指定系统将首先尝试压缩一个长的数据值,并且将在数据仍然太长的情况下把值移出主表行。
- external允许值被移出主表,但是系统将不会尝试对它进行压缩。
- main允许压缩,但是不鼓励把值移出主表(如果没有其他办法让行的大小变得合适,具有这种存储策略的数据项仍将被移出主表,但比起extended以及external项来,这种存储策略的数据项会被优先考虑保留在主表中)。
除plain之外所有的storage值都暗示该数据类型的函数能处理被TOAST过的值。指定的值仅决定一种可TOAST数据类型的列的默认TOAST存储策略,用户可以使用ALTER TABLE SET STORAGE为列选取其他策略。
- like_type
可选参数。与新类型具有相同表达的现有数据类型的名称。会从这个类型中复制internallength、passedbyvalue、alignment以及storage的值( 除非在这个CREATE TYPE命令的其他地方用显式说明覆盖)。
当新类型的底层实现是以一种现有的类型为参考时,用这种方式指定表达特别有用。
- category
可选参数。这种类型的分类码(一个ASCII字符)。默认是“用户定义类型”的'U'。为了创建自定义分类, 也可以选择其他ASCII字符。
- preferred
可选参数。如果这种类型是其类型分类中的优先类型则为TRUE,否则为FALSE。默认为FALSE。在一个现有类型分类中创建一种新的优先类型要非常谨慎,因为这可能会导致很大的改变。
category和preferred参数可以被用来帮助控制在混淆的情况下应用哪一种隐式造型。每一种数据类型都属于一个用单个ASCII字符命名的分类,并且每一种类型可以是其所属分类中的“首选”。当有助于解决重载函数或操作符时,解析器将优先造型到首选类型(但是只能从同类的其他类型造型)。对于没有隐式转换到或来自任意其他类型的类型,让这些设置保持默认即可。不过,对于有隐式转换的相关类型的组,把它们都标记为属于同一个类别并且选择一种或两种“最常用”的类型作为该类别的首选通常是很有用的。在把一种用户定义的类型增加到一个现有的内建类别(例如,数字或者字符串类型)中时,category参数特别有用。不过,也可以创建新的全部是用户定义类型的类别。对这样的类别,可选择除大写字母之外的任何ASCII字符。
- default
可选参数。数据类型的默认值。如果被省略,默认值是空。
如果用户希望该数据类型的列被默认为某种非空值,可以指定一个默认值。默认值可以用DEFAULT关键词指定(这样一个默认值可以被附加到一个特定列的显式DEFAULT子句覆盖)。
- element
可选参数。被创建的类型是一个数组,element指定了数组元素的类型。例如,要定义一个4字节整数的数组(int4),应指定ELEMENT = int4。
- delimiter
可选参数。指定这种类型组成的数组中分隔值的定界符。
可以把delimiter设置为一个特定字符,默认的定界符是逗号(,)。注意定界符是与数组元素类型相关的,而不是数组类型本身相关。
- collatable
可选参数。如果这个类型的操作可以使用排序规则信息,则为TRUE。默认为FALSE。
如果collatable为TRUE,这种类型的列定义和表达式可能通过使用COLLATE子句携带有排序规则信息。在该类型上操作的函数的实现负责真正利用这些信息,仅把类型标记为可排序的并不会让它们自动地去使用这类信息。
- label
可选参数。与枚举类型的一个值相关的文本标签,其值为长度不超过64个字符的非空字符串。
在创建用户定义类型的时候,GaussDB(DWS)会自动创建一个与之关联的数组类型,其名字由该元素类型的名字前缀一个下划线组成。
示例
示例一:创建一种复合类型,建表并插入数据以及查询。
1 2 3 4 5 6 7 |
CREATE TYPE compfoo AS (f1 int, f2 text); CREATE TABLE t1_compfoo(a int, b compfoo); CREATE TABLE t2_compfoo(a int, b compfoo); INSERT INTO t1_compfoo VALUES(1,(1,'demo')); INSERT INTO t2_compfoo SELECT * FROM t1_compfoo; SELECT (b).f1 FROM t1_compfoo; SELECT * FROM t1_compfoo t1 JOIN t2_compfoo t2 ON (t1.b).f1=(t1.b).f1; |
示例二:创建一个枚举类型,并在表定义中使用它。
1 2 3 4 5 |
CREATE TYPE bugstatus AS ENUM ('create', 'modify', 'closed'); DROP TABLE IF EXISTS customer; CREATE TABLE customer (name text,current_bugstatus bugstatus); INSERT INTO customer VALUES ('type','create'); SELECT * FROM customer WHERE current_bugstatus = 'create'; |
示例三:编译.so文件,并创建shell类型。
1
|
CREATE TYPE complex; |
这个语句的作用是为要定义的类型创建了一个占位符,这样允许在定义其I/O函数时引用该类型。现在可以定义I/O函数,需要注意的是在创建函数时function必须声明为NOT FENCED模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
CREATE FUNCTION complex_in(cstring) RETURNS complex AS 'filename' LANGUAGE C IMMUTABLE STRICT not fenced; CREATE FUNCTION complex_out(complex) RETURNS cstring AS 'filename' LANGUAGE C IMMUTABLE STRICT not fenced; CREATE FUNCTION complex_recv(internal) RETURNS complex AS 'filename' LANGUAGE C IMMUTABLE STRICT not fenced; CREATE FUNCTION complex_send(complex) RETURNS bytea AS 'filename' LANGUAGE C IMMUTABLE STRICT not fenced; |
最后,提供该数据类型的完整定义:
1 2 3 4 5 6 7 8 |
CREATE TYPE complex ( internallength = 16, input = complex_in, output = complex_out, receive = complex_recv, send = complex_send, alignment = double ); |
input、output、receive及send函数对应的C函数定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
--定义结构体Complex如下: typedef struct Complex { double x; double y; } Complex; --定义input函数: PG_FUNCTION_INFO_V1(complex_in); Datum complex_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); double x, y; Complex *result; if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for complex: \"%s\"", str))); result = (Complex *) palloc(sizeof(Complex)); result->x = x; result->y = y; PG_RETURN_POINTER(result); } --定义output函数: PG_FUNCTION_INFO_V1(complex_out); Datum complex_out(PG_FUNCTION_ARGS) { Complex *complex = (Complex *) PG_GETARG_POINTER(0); char *result; result = (char *) palloc(100); snprintf(result, 100, "(%g,%g)", complex->x, complex->y); PG_RETURN_CSTRING(result); } --定义receive函数: PG_FUNCTION_INFO_V1(complex_recv); Datum complex_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); Complex *result; result = (Complex *) palloc(sizeof(Complex)); result->x = pq_getmsgfloat8(buf); result->y = pq_getmsgfloat8(buf); PG_RETURN_POINTER(result); } --定义send函数: PG_FUNCTION_INFO_V1(complex_send); Datum complex_send(PG_FUNCTION_ARGS) { Complex *complex = (Complex *) PG_GETARG_POINTER(0); StringInfoData buf; pq_begintypsend(&buf); pq_sendfloat8(&buf, complex->x); pq_sendfloat8(&buf, complex->y); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } |