第 32 章 扩展 SQL:类型
如前所述,Postgres中有两类类型:基本类型(用编程语言定义)和组合类型(实例)。本节中直到索引接口之前的示例可以在
complex.sql和complex.c中找到。组合类型的示例在funcs.sql中。
32.1. 用户定义的类型
32.1.1. 用户定义类型所需的函数
用户定义的类型必须始终具有输入和输出函数。这些函数决定该类型在字符串中如何表示(供用户输入和输出给用户)以及在内存中如何组织。输入函数接受一个以空字符结尾的字符串作为输入,并返回该类型的内部(内存中)表示。输出函数接受该类型的内部表示,并返回一个以空字符结尾的字符串。假设我们想定义一个表示复数的 complex 类型。很自然,我们会选择用下面的C结构在内存中表示复数:
typedef struct Complex {
double x;
double y;
} Complex;并选择形如 (x,y) 的字符串作为外部字符串表示。这些函数通常不难编写,输出函数尤其如此。不过,有几点需要记住:
在定义你的外部(字符串)表示时,请记住:你最终必须为该表示编写一个完整而健壮的解析器作为输入函数!
Complex * complex_in(char *str) { double x, y; Complex *result; if (sscanf(str, " ( [percnt]lf , [percnt]lf )", &x, &y) != 2) { elog(WARN, "complex_in: error in parsing 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, "([percnt]g,[percnt]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会自动提供对该类型数组的支持。由于历史原因,数组类型的名称与用户定义类型相同,只是在前面加上下划线字符 _。组合类型不需要在其上定义任何函数,因为系统已经知道它们内部是什么样子。
32.1.2. 大对象
到此为止讨论的类型都是"小"对象——也就是说,它们的大小小于 8KB。
注意
1024 长字 == 8192 字节。实际上,类型必须显著小于 8192 字节,因为Postgres的元组和页开销也必须放进这个 8KB 的限制内。实际能放下的值取决于机器体系结构。
如果你需要一个更大的类型,比如用于文档检索系统或存储位图,你将需要使用Postgres的大对象接口。