Postgresql:可变长度的用户定义数据类型存储设置

时间:2014-02-07 06:48:35

标签: postgresql

我根据文档(http://www.postgresql.org/docs/9.0/static/xtypes.html)在postgresql中定义一个可变长度的用户定义数据类型

C定义:

typedef struct MyType {
    char    vl_len_[4];
    char    data[1];
} mytype;

CREATE TYPE语句

CREATE TYPE mytype;
CREATE FUNCTION mytype_in(cstring) RETURNS mytype AS 'mytype' LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION mytype_out(mytype) RETURNS cstring AS 'mytype' LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION mytype_recv(internal) RETURNS mytype AS 'mytype' LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION mytype_send(mytype) RETURNS bytea AS 'mytype' LANGUAGE C IMMUTABLE STRICT;

CREATE TYPE mytype (
 internallength = VARIABLE,
 input = mytype_in,
 output = mytype_out,
 receive = mytype_recv,
 send = mytype_send,
 alignment = int4
 ,storage = plain
);

我还用C语言定义了所有这些功能。但是,由于我的数据可能很长,我将存储从普通更改为外部扩展。然后输出错误的结果。我需要在C函数中使用一些TOAST函数吗?

例如:

我有一个运算符来合并两个值,如下所示:

PG_FUNCTION_INFO_V1(mytype_add);

Datum
mytype_add(PG_FUNCTION_ARGS)
{
    mytype *anno1 = (mytype *) PG_GETARG_POINTER(0);
    mytype *anno2 = (mytype *) PG_GETARG_POINTER(1);
    mytype    *result;
    int     newsize;

    newsize = VARSIZE(anno1) + VARSIZE(anno2) - VARHDRSZ;
    result = (mytype *) palloc(newsize);
    SET_VARSIZE(result, newsize);
    memcpy(result->data, anno1->data, VARSIZE(anno1) - VARHDRSZ);
    memcpy((result->data + VARSIZE(anno1) - VARHDRSZ), anno2->data, VARSIZE(anno2) - VARHDRSZ);

    PG_RETURN_POINTER(result);
}

anno1->数据中的值(12个字节,3个整数)为:10,-1,-1,anno2->数据中的值为:20,-1,-1

因此result->数据(24字节)中的值为:10,-1,-1,20,-1,-1

如果我将存储设置为普通,我得到了正确的结果。如果我将存储设置为外部,则输出完全错误:-256,-1,1317887 ......

非常感谢任何人都可以提供任何暗示。我花了很多时间在这个

2 个答案:

答案 0 :(得分:0)

您未能TOAST输入Datum。所以你要连接一个压缩形式,或者可能是指向外部存储的指针,而不是原始数据。

我认为你需要使用PG_GETARG_VARLENA_P(0)来确保在使用之前解开基准。不过,我没有直接使用TOAST和varlena类型。

我不清楚为什么你要使用与struct varlena相同的结构声明自己的类型,而不仅仅使用Datum和基础struct varlena来获取可变长度的基准。从:

开始
struct varlena *anno1 = PG_GETARG_VARLENA_P(0);

另外,为什么你要重新实现intarray(严重的,即使用char数组)?请read this relevant articlethis one

答案 1 :(得分:0)

再添加一件事,主要区别在于保存的结构不同,前缀表示数据的长度。

因此,在编写输入函数时,在数据启动之前需要实现一个4字节的头,并使用“SET_VARSIZE(PTR,len)”来改变4字节头的值。

另一方面,当检索数据时,您需要使用“PG_GETARG_VARLENA_P(n)”,并且检索到的结果还将包含指示长度的4字节标头。您可以使用“VARSIZE_4B(PTR)”获取长度,它将返回数据的字节长度。

为了总结和给出示例代码,我们假设我们想要存储一个未知数量的结构复合体:

typedef struct Complex 
{
    double      x;
    double      y;
} Complex;

因此在收到输入字符串后,我们决定需要存储n个struct。因此,分配内存:

struct varlena* result = (struct varlena*)palloc(n * sizeof(Complex) + 4);

如文档中所述,我们需要编辑前4个字节并设置长度:

SET_VARSIZE(result, n * sizeof(Complex));

以下字节,我们应该为它们分配值,记住地址应该与你的系统结构对齐:

Complex * a = (Complex*)((__int64)result + 4);
for (int i = 0; i < n; i++) {
    a[i].x = input[i];
    a[i].y = input[i];
}

最后,数据应存储在:

PG_RETURN_POINTER(result);

要检索数据,需要使用

struct varlen *b = PG_GETARG_VARLENA_P(0);

如上所述,结果也是前面有4个字节表示长度,输出函数可能是:

Complex *c = (Complex *)(&(b->vl_dat));
char *result;
int n = VARSIZE_ANY_EXHDR(b) / sizeof(Complex);
for (int i = 0; i < n; i++) {
    result = psprintf("(%g;%g)", c[i].x, c[i].y);
}
PG_RETURN_CSTRING(result);

我没有测试过这个确切的代码但是类似的代码,结果应该没问题。如果有人可以对此添加评论或纠正我所犯的任何错误,那就太好了。这也是我自己的参考。