是的,又是我,不要脸的又来混经验了。我们知道PostgreSQL是一个高度可扩展的数据库,这次我聊聊如何在PostgreSQL里创建一个优雅的type,如何理解优雅?大概就是不仅仅是type本身,其它相关的“服务”都得跟上,要像数据库自带的type一样想怎么用怎么用。
好的,我们开始。
1. CREATE TYPE
PostgreSQL能够被扩展成支持新的数据类型。这一节我们先说说如何定义新的基本类型,这里的type是被定义在SQL语言层面之下的数据类型。创建一种新的基本类型要求使用低层语言(通常是 C)实现在该类型上操作的函数。
例子来自于源代码src/tutorial目录下的complex.sql和complex.c。运行这些例子的指令可以在该目录的README文件中找到。
CREATE TYPE的语法在这里:http://www.postgres.cn/docs/9.5/sql-createtype.html
在PostgreSQL中,一种用户定义的类型必须总是具有输入和输出函数。这些函数决定该类型如何出现在字符串中(用于用户输入或者对用户的输出)以及如何在内存中组织该类型.
关于以上提到的这些,我先不要脸的画一个图吧:
outside(screen) inside(disk file)
| -------------------------> |
| complex_in |
| |
| |
| <------------------------- |
| complex_out |
比如我们要创建一个复数类型complex,它的C语言描述如下:
typedef struct Complex {
double x;
double y;
} Complex;
在用C语言实现时我们将需要让它成为一种传引用类型,因为它没办法放到一个单一的Datum值中(因为Datum里面只能放单一的基本数据类型)。
接着为美观易懂起见,我们选择字符串形式的"(x,y)作为该类型的外部字符串表达"。
也就是说,我们用"(x,y)"表示一个complex,并且在使用时我们这样书写,在查询时,数据库返回的也是这样的字符串。
我们写一个简单的输入函数吧:
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);
}
再来一个输出函数
PG_FUNCTION_INFO_V1(complex_out);
Datum
complex_out(PG_FUNCTION_ARGS)
{
Complex *complex = (Complex *) PG_GETARG_POINTER(0);
char *result;
result = psprintf("(%g,%g)", complex->x, complex->y);
PG_RETURN_CSTRING(result);
}