PostgreSQL C 扩展:函数调用之间的持久数据



我正在尝试了解如何编写C函数来为PostgreSQl创建扩展,但到目前为止我迷失了方向。

我想写一个非常基本的函数,它接受一个整数并将其递增 10 倍。我计划将其用作使用内存上下文跨调用设置返回函数和数据持久性的示例。我的问题是调用之间的数据持久性:每个结果调用一次函数,每次调用之间刷新内存,这意味着我的整数消失并且最终结果不正确。

这是我到目前为止写的:

/**
* Function that returns a set of integers (argument +10, step 1)
* Arguments:
*  - int32 i = the original number to increment
*/
PG_FUNCTION_INFO_V1(addTen);
Datum addTen(PG_FUNCTION_ARGS) {
int32            i;
FuncCallContext *funcctx;
int              call_cntr;
int              max_calls;
// Code executed only on first call of the function
if (SRF_IS_FIRSTCALL()) {
MemoryContext oldcontext;
// Initializing the function context for cross call persistence
funcctx = SRF_FIRSTCALL_INIT();
// Context memory for multi calls
oldcontext = MemoryContextSwitchTo(funcctx -> multi_call_memory_ctx);
// Getting argument (original integer)
if (PG_ARGISNULL(0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Table cannot be NULL")));
i = (int32) palloc(sizeof(int32));
i = PG_GETARG_INT32(0);
// Alloacting space to save the integer and make it persistent between calls
funcctx->user_fctx = &i;
// Maximum number of calls
funcctx -> max_calls = 10;
MemoryContextSwitchTo(oldcontext);
}
// Code executed on each call (first one included)
// Retrieving values from function context
funcctx = SRF_PERCALL_SETUP();
call_cntr = funcctx->call_cntr;
max_calls = funcctx -> max_calls;
if (call_cntr < max_calls) {
int32* temp = funcctx->user_fctx;
i = *temp;
SRF_RETURN_NEXT(funcctx, i + call_cntr);
} else {    // Done
SRF_RETURN_DONE(funcctx);
}
}

如您所见,这是一个非常愚蠢的功能。我可以简单地在每次调用中使用 PG_GETARG_INT32(0),瞧,它会起作用。但我真的很想了解我应该如何在调用之间保留数据,这个简单的例子似乎是一个很好的方法。

我在这里尝试的是使用函数上下文的 user_fctx 字段在每次调用时恢复我的整数。问题是它是一个指针,它指向的整数在调用之间被擦除。我应该如何告诉 Postgres 不要删除我的整数,或者我应该在哪里存储它?

参考:PostgreSQL 文档中的 C 语言函数。

函数中的问题

i = (int32) palloc(sizeof(int32));

palloc() 返回一个指针,而不是一个整数。将其返回值分配给整数是一个错误。

i = PG_GETARG_INT32(0);

它本身是正确的,但它覆盖了之前放入i的值,然后肯定会丢失。

// Alloacting space to save the integer and make it persistent between calls
funcctx->user_fctx = &i;

i是一个局部变量,不会在调用中保留。存储其地址以在后续函数调用中重用它是一个错误。

解决 方案

要分配空间以在调用之间保留 int32 值,您需要如下所示的内容:

funcctx->user_fctx = (void*)palloc(sizeof(int32));

根据文档,funcctx->user_fctx属于void*类型,因此您需要在每次使用时将其转换为它指向的类型。

要分配它,要知道funcctx->user_fctx已为 int32 变量分配:

*((int32*)funcctx->user_fctx) = PG_GETARG_INT32(0);

也可以使用相同的语法读取它:

i = *((int32*)funcctx->user_fctxt);

您分两步编写它的方式也是正确的,等效于上一行:

int32* temp = funcctx->user_fctx;
i = *temp;

最新更新