c - 在外部 C 库中实现的 PostgreSQL 聚合函数(崩溃服务器)

标签 c postgresql

我已经为 PG 编写了一个 C 扩展库,使用 V1 调用约定。当我调用聚合函数时,它使 postgres 服务器进程崩溃。我已使用 gdb 调试服务器进程并找到了发生 Seg-V 的位置。

这是由试图访问无效地址引起的。但我不明白的是,内存是早些时候成功分配的。不过,从代码中的注释来看,我怀疑内存正在被收集/释放,同时仍在使用中。我对 postgres 的内部结构知之甚少——但这似乎是问题的可能原因。

我已经包含了导致崩溃的函数的代码,并突出显示了导致 Seg-V 的行。我是否使用正确的参数调用 MemoryContextAlloc?

static PGARRAY *GetPGArray(int4 state, int fAdd);    
static PGARRAY *ShrinkPGArray(PGARRAY * p);

Datum       float8_agg_state(PG_FUNCTION_ARGS);    
Datum       float8_agg_final_count(PG_FUNCTION_ARGS);    
Datum       float8_agg_final_array(PG_FUNCTION_ARGS);    
Datum       float8_enum(PG_FUNCTION_ARGS);        

PG_FUNCTION_INFO_V1(float8_agg_state);    
PG_FUNCTION_INFO_V1(float8_agg_final_count);    
PG_FUNCTION_INFO_V1(float8_agg_final_array);    
PG_FUNCTION_INFO_V1(float8_enum);

/*    
 * Manage the aggregation state of the array    
 *    
 * Need to specify a suitably long-lived memory context, or it will vanish!    
 * PortalContext isn't really right, but it's close enough (famous last words ...).    
 */

static PGARRAY *    
GetPGArray(int4 state, int fAdd)    
{    
    PGARRAY    *p = (PGARRAY *) state;

    if (!state)    
    {    
        /* New array */    
        int cb = PGARRAY_SIZE(START_NUM);       

        p = (PGARRAY *) MemoryContextAlloc(PortalContext, cb);    
        p->a.vl_len_ = cb;    
        p->a.ndim = 0;    
        p->a.dataoffset = 0;

#ifndef PG_7_2    
        p->a.elemtype = FLOAT8OID;    
#endif

        p->items = 0;    
        p->lower = START_NUM; 
    }
    else if (fAdd)    
    {       
           /* Ensure array has space */

           /* SEG-V fault on the line below */
        if (p->items >= p->lower)    
        {    
            PGARRAY *pn;    
            int n = p->lower + p->lower;   
            int cbNew = PGARRAY_SIZE(n);

            pn = (PGARRAY *) repalloc(p, cbNew);    
            pn->a.vl_len_ = cbNew;    
            pn->lower = n;    
            return pn;   
        }    
    }

    return p;    
}

谁能看出为什么代码是 SG-V?

[[编辑]]

我的后台PG服务器是v8.4.9

最佳答案

应该还有更多问题 - 注册错误、返回数据格式错误、返回数据内存上下文错误 - 您可以在 array_agg 实现中找到好的模式 http://doxygen.postgresql.org/array__userfuncs_8c_source.html

00477 array_agg_transfn(PG_FUNCTION_ARGS)
00478 {
00479     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
00480     MemoryContext aggcontext;
00481     ArrayBuildState *state;
00482     Datum       elem;
00483 
00484     if (arg1_typeid == InvalidOid)
00485         ereport(ERROR,
00486                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00487                  errmsg("could not determine input data type")));
00488 
00489     if (!AggCheckCallContext(fcinfo, &aggcontext))
00490     {
00491         /* cannot be called directly because of internal-type argument */
00492         elog(ERROR, "array_agg_transfn called in non-aggregate context");
00493     }
00494 
00495     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
00496     elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
00497     state = accumArrayResult(state,
00498                              elem,
00499                              PG_ARGISNULL(1),
00500                              arg1_typeid,
00501                              aggcontext);
00502 
00503     /*
00504      * The transition type for array_agg() is declared to be "internal", which
00505      * is a pass-by-value type the same size as a pointer.  So we can safely
00506      * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
00507      */
00508     PG_RETURN_POINTER(state);
00509 }

这是针对 9.1 的 - 如果您有旧版本,请查看相关源代码。

关于c - 在外部 C 库中实现的 PostgreSQL 聚合函数(崩溃服务器),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8730756/

相关文章:

c - 使用 strtok 时的意外行为

database - Postgres 复制

database - 批量更新 postgresql 序列

java - Postgres 9.4 - 连接被拒绝 - 当我从远程 PC 连接时

php - YII 无法读取 Postgres 9.4 中的 BIGINT。静默转换为 MAX_INT。适用于 9.5

有人可以解释一下这个 "kth smallest integer in an unsorted array"代码吗? - C

c - 我在哪里放置 perror ("wait") 和 fork 代码

c - fftw 与 complex.h 在 c 中的接口(interface)

c - 使用埃拉托斯特尼筛法求素数之和

sql - 从 csv 导出数据时执行触发器