正如前面所说, 在 Postgres里有两种数据类型: 基本类型(在编程语言里定义)和复合类型(记录). 本章到索引接口章节的例子可以在 complex.sql 和 complex.c 里找到.复合类型的例子在 funcs.sql 里.
一个用户定义的类型总是有输入和输出函数. 这些函数决定该类型如何在字串里出现(让用户输入和输出给用户)和 类型如何在存储器里组织. 输入函数以一个以空(null)为分隔符的字符串为输入并且返回该类型的 内部(在存储器里)的表现形式.输出类型以该类型的内部表现形式 为输入并且返回一个以空(null)为分隔符的字符串. 假设我们要定义一个复数类型用来表示复数.通常,我们选用下面的 C 结构来在存储器里表现复数:
typedef struct Complex { double x; double y; } Complex;并且以 (x,y) 形式的字串做为外部的表现形式. 这些函数通常比较容易写,尤其是输出函数.不过,我们还是要注意几点:
当定义你的外部(字符串)表现形式时, 要注意你最后必须为该表现形式写一个完整而且健壮的 分析器作为你的输入函数!
Complex * complex_in(char *str) { double x, y; Complex *result; if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2) { elog(ERROR, "complex_in: error in parsing %s", str); return NULL; } result = (Complex *)palloc(sizeof(Complex)); result->x = x; result->y = y; return (result); }输出函数可以简单的就是:
char * complex_out(Complex *complex) { char *result; if (complex == NULL) return(NULL); result = (char *) palloc(60); sprintf(result, "(%g,%g)", complex->x, complex->y); return(result); }
你应该把你的输入和输出函数做的互为逆(函数).如果你不这样做, 你就可能在需要把数据输出来在装载回去时碰到很严重的问题 (比如,输入到别人在另外的计算机上的数据库中去). 当涉及到浮点数时,这是非常普遍的问题.
要定义 complex 类型,我们要在创建该类型前先创建两个用户定义函数 complex_in 和 complex_out:
CREATE FUNCTION complex_in(opaque) RETURNS complex AS 'PGROOT/tutorial/obj/complex.so' LANGUAGE 'c'; CREATE FUNCTION complex_out(opaque) RETURNS opaque AS 'PGROOT/tutorial/obj/complex.so' LANGUAGE 'c'; CREATE TYPE complex ( internallength = 16, input = complex_in, output = complex_out );
正如我们前面讨论的,Postgres 完全支持基本类型的数组.另外 Postgres 同样还支持用户定义类型的数组. 当你定义类型时, Postgres 自动提供对该类型的数组的支持. 因为历史原因, 数组类型的类型名是与类型同名字串前面加个下划线 _ . 不需要为复合类型定义任何函数,因为系统已经知道它们在里面看起来象什么.
如果你的数据类型的尺寸可能超过几百个字节大小(内部形式), 那么你应该仔细地把它们标记为可以 TOAST 的. 要做这些事情,内部表现形式必须遵循变长数据的标准布局: 头四个字节必须是一个 int32,保存这个数据的以字节记的长度 (包括长度本身).然后,你的所有接受该类型数值的函数都必须 仔细地在所提供的数值上调用 pg_detoast_datum() --- 如果你的函数不是严格的,那么在检查过该数值不是 NULL 之后. 最后,在给出 CREATE TYPE 命令的时候选择合适的存储选项.