【问题标题】:Returning more than one row crashes on PostgreSQL在 PostgreSQL 上返回不止一行崩溃
【发布时间】:2025-12-27 19:05:08
【问题描述】:

在 PostgreSQL 上,我在 C 中实现了以下存储过程:

extern "C" DLLEXPORT Datum
selectServeralRows(PG_FUNCTION_ARGS)
{
    FuncCallContext     *funcctx;
    int                  call_cntr;
    int                  max_calls;
    TupleDesc            tupdesc;
    AttInMetadata       *attinmeta;

    /* stuff done only on the first call of the function */
    if (SRF_IS_FIRSTCALL())
    {
        MemoryContext   oldcontext;

        /* create a function context for cross-call persistence */
        funcctx = SRF_FIRSTCALL_INIT();

        /* switch to memory context appropriate for multiple function calls */
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

        /* total number of tuples to be returned */
        funcctx->max_calls = 1;

        /* Build a tuple descriptor for our result type */
        if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("function returning record called in context "
                        "that cannot accept type record")));

        /*
         * generate attribute metadata needed later to produce tuples from raw
         * C strings
         */
        attinmeta = TupleDescGetAttInMetadata(tupdesc);
        funcctx->attinmeta = attinmeta;

        MemoryContextSwitchTo(oldcontext);
    }

    /* stuff done on every call of the function */
    funcctx = SRF_PERCALL_SETUP();

    if (funcctx->call_cntr < funcctx->max_calls)    /* do when there is more left to send */
    {
        Datum* val = (Datum*)palloc(2 * sizeof(Datum));
        HeapTuple    tuple;
        Datum        result;
        bool    nulls[2]={false,false};

        char * n = new char[2]; 
        n[0] = '1';
        n[1] = '\0';
        char * m = new char[2];
        m[0] = '2';
        n[1] = '\0';

        val[0] = CStringGetTextDatum(m);
        val[1] = CStringGetTextDatum(n);

        /* build a tuple */
        tuple = heap_form_tuple(tupdesc, val, nulls);

        /* make the tuple into a datum */
        result = TupleGetDatum(funcctx->slot, tuple);

        /* clean up (this is not really necessary) */

       SRF_RETURN_NEXT(funcctx, result);
    }
else
       SRF_RETURN_DONE(funcctx);
}

代码和这里差不多:http://www.postgresql.org/docs/8.4/static/xfunc-c.html

如果过程只返回一行,一切都很好,并且只有一行的表是所需的。但是如果这样改一行

/* total number of tuples to be returned */
funcctx->max_calls = 2;

查询的执行因以下消息而崩溃:

程序接收到信号EXC_BAD_ACCESS,无法访问内存。 原因:KERN_INVALID_ADDRESS 地址:0x000000000000041a heap_form_tuple()中的0x0000000100002d8b

单步执行代码显示在崩溃之前没有任何内容是无效指针,所以我有点无能为力。还有什么我监督的吗?

编辑: 函数在psql中是这样调用的:

select (selectServeralRows()).*

编辑: 函数的SQL定义:

CREATE OR REPLACE FUNCTION selectServeralRows()
RETURNS TABLE(k character varying(20), j character varying(20)) AS
'/opt/local/lib/postgresql84/Debug/libSeveralRows', 'selectServeralRows'
LANGUAGE c STABLE STRICT;

【问题讨论】:

  • 注意:C 中没有 new 运算符。
  • 我看到一个错误: Datum* val = (Datum*)palloc(2 + sizeof(Datum));可能你会使用 * 而不是 +
  • @wildplasser,实际上这是用 gcc 编译的。尽管如此,我还是将其更改为使用 malloc 并且效果相同。
  • @PavelStehule 谢谢。我也改变了那个,但最后没有帮助。
  • 你为什么不使用malloc的palloc insted呢?顺便说一句:palloc(2 + sizeof(Datum)); 看起来也很可疑。

标签: c postgresql crash


【解决方案1】:

看来你省略了一个

 tuple = BuildTupleFromCStrings(funcctx->attinmeta, val);

TupleGetDatum(...) 调用之前调用。元组变量仍然未初始化,除了第一次调用。

另外,Datum* val = (Datum*)palloc(2 * sizeof(Datum)); 应该是

char **val;
val = palloc (2 * sizeof *val);

n 和 m 数组也可以;

char n[2] ="1", m[2] = "2";
val[0] = n;
val[1] = m;

您可以在调用后释放内存heap_form_tuple(tupdesc, val, nulls);

pfree(val);

恕我直言,'val' 也可以是自动(“堆栈”)变量,就像 n[] 和 m[]。

【讨论】: