为什么我的PostgreSQL C函数会崩溃?
我们使用了两个传统的聚合函数,它们连接文本值,由一个条(为什么我的PostgreSQL C函数会崩溃?,c,postgresql,C,Postgresql,我们使用了两个传统的聚合函数,它们连接文本值,由一个条(|)或逗号(,)进行增量。这些函数本质上是postgresql9.0中出现的string\u agg()的非常缓慢的实现 为了获得string\u agg() 不幸的是,我不是一个C程序员 无论如何,在postgresql源代码的varlena.c中找到string_agg()源代码后,我开始重用该代码: #include "postgres.h" #include <
|
)或逗号(,
)进行增量。这些函数本质上是postgresql9.0中出现的string\u agg()
的非常缓慢的实现
为了获得string\u agg()
不幸的是,我不是一个C程序员
无论如何,在postgresql源代码的varlena.c
中找到string_agg()
源代码后,我开始重用该代码:
#include "postgres.h"
#include <ctype.h>
#include <syslog.h>
#include <sys/types.h>
#include <unistd.h>
#include "lib/stringinfo.h"
#include "fmgr.h"
#include "utils/builtins.h"
PG_MODULE_MAGIC;
/*
Compile this module in the following way:
gcc -I $(pg_config --includedir-server) -fpic -c vs_funcs.c;
gcc -shared -o $(pg_config --pkglibdir)/vs_funcs.so vs_funcs.o;
This will also place the compiled module in the correct location.
*/
// Code below taken directly or modified from varlena.c in postgresl source
/*
* appendStringInfoText
*
* Append a text to str.
* Like appendStringInfoString(str, text_to_cstring(t)) but faster.
*/
static void
appendStringInfoText(StringInfo str, const text *t)
{
appendBinaryStringInfo(str, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
}
// This is largely copied from the code for the string_agg function
#define MAKE_STR_AGG(AGG_NAME, AGG_DELIM) \
static StringInfo \
make_ ## AGG_NAME ##_state(FunctionCallInfo fcinfo) \
{ \
StringInfo state; \
MemoryContext aggcontext; \
MemoryContext oldcontext; \
if (!AggCheckCallContext(fcinfo, &aggcontext)) \
{ \
elog(ERROR, "#AGG_NAME called in non-aggregate context"); \
} \
oldcontext = MemoryContextSwitchTo(aggcontext); \
state = makeStringInfo(); \
MemoryContextSwitchTo(oldcontext); \
return state; \
} \
PG_FUNCTION_INFO_V1(AGG_NAME ## _transfn); \
Datum \
AGG_NAME ## _transfn(PG_FUNCTION_ARGS) \
{ \
{ \
StringInfo state; \
state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); \
if (!PG_ARGISNULL(1)) \
{ \
if (state == NULL) \
state = make_ ## AGG_NAME ## _state(fcinfo); \
else if (!PG_ARGISNULL(2)) \
appendStringInfoText(state, string_to_text(#AGG_DELIM)); \
appendStringInfoText(state, PG_GETARG_TEXT_PP(1)); \
} \
PG_RETURN_POINTER(state); \
}\
PG_FUNCTION_INFO_V1(AGG_NAME ## _finalfn); \
Datum \
AGG_NAME ## _finalfn(PG_FUNCTION_ARGS) \
{ \
StringInfo state; \
Assert(AggCheckCallContext(fcinfo, NULL)); \
state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); \
if (state != NULL) \
PG_RETURN_TEXT_P(cstring_to_text(state->data)); \
else \
PG_RETURN_NULL(); \
}
/*
* bar_list - Concatenates values and returns string.
*
* Syntax: bar_list(value text) RETURNS text
*
* Note: Any NULL values are ignored. The first-call delimiter isn't
* actually used at all, and on subsequent calls the delimiter precedes
* the associated value.
*/
MAKE_STR_AGG(bar_list, '|');
/*
* comma_list - Concatenates values and returns string.
*
* Syntax: comma_list(value text) RETURNS text
*
* Note: Any NULL values are ignored. The first-call delimiter isn't
* actually used at all, and on subsequent calls the delimiter precedes
* the associated value.
*/
MAKE_STR_AGG(comma_list, ',');
结果库中的符号:
vezult@cruella-devel:~/Development/VistaShare/oct_git/utilities$ nm vs_funcs.so
U AggCheckCallContext
U CurrentMemoryContext
00000000000009dc t MemoryContextSwitchTo
0000000000000ec0 r Pg_magic_data.8975
0000000000000a06 T Pg_magic_func
0000000000201138 a _DYNAMIC
00000000002012e8 a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
0000000000201118 d __CTOR_END__
0000000000201110 d __CTOR_LIST__
0000000000201128 d __DTOR_END__
0000000000201120 d __DTOR_LIST__
0000000000001108 r __FRAME_END__
0000000000201130 d __JCR_END__
0000000000201130 d __JCR_LIST__
0000000000201348 A __bss_start
w __cxa_finalize@@GLIBC_2.2.5
0000000000000de0 t __do_global_ctors_aux
0000000000000930 t __do_global_dtors_aux
0000000000201340 d __dso_handle
0000000000000ea0 r __func__.9026
0000000000000e80 r __func__.9096
w __gmon_start__
0000000000201348 A _edata
0000000000201358 A _end
0000000000000e18 T _fini
0000000000000868 T _init
U appendBinaryStringInfo
0000000000000a13 t appendStringInfoText
0000000000000bec T bar_list_finalfn
0000000000000b34 T bar_list_transfn
0000000000000910 t call_gmon_start
0000000000000d82 T comma_list_finalfn
0000000000000cca T comma_list_transfn
0000000000201348 b completed.6341
U cstring_to_text
0000000000201350 b dtor_idx.6343
U elog_finish
U elog_start
00000000000009b0 t frame_dummy
U makeStringInfo
0000000000000aab t make_bar_list_state
0000000000000c41 t make_comma_list_state
0000000000000e9c r my_finfo.9039
0000000000000e98 r my_finfo.9072
0000000000000e78 r my_finfo.9109
0000000000000e74 r my_finfo.9142
U pg_detoast_datum_packed
0000000000000bdf T pg_finfo_bar_list_finalfn
0000000000000b27 T pg_finfo_bar_list_transfn
0000000000000d75 T pg_finfo_comma_list_finalfn
0000000000000cbd T pg_finfo_comma_list_transfn
我在postgres中创建函数:
CREATE FUNCTION bar_list_transfn(text, text) RETURNS text AS 'vs_funcs', 'bar_list_transfn' language 'C';
当我运行它时,它会因为一个分段错误而导致postgres崩溃:
2011-09-09 10:02:15 EDT LOG: server process (PID 15881) was terminated by signal 11: Segmentation fault
所以,在我看来,我的代码应该与内置函数非常相似,所以我不确定它为什么不工作。老实说,我不知道编译时警告是否重要,或者真正的问题是否在别处
我做错了什么?看起来您的SQL代码使用了错误的类型。从源代码(src/include/catalog/pg_proc.h
):
特别是:
... string_agg_transfn ... 2281 "2281 25 25" ...
... string_agg_finalfn ... 25 "2281" ...
... string_agg ... 25 "25 25" ...
第一个OID是返回类型。后面的OID是参数类型。可以通过查询目录对这些OID进行解码:
joey=# SELECT oid, typname FROM pg_type WHERE oid IN (25, 2281);
oid | typname
------+----------
25 | text
2281 | internal
(2 rows)
因此,正确的SQL语句应该是:
CREATE FUNCTION bar_list_transfn(internal, text)
RETURNS internal
AS 'vs_funcs', 'bar_list_transfn'
LANGUAGE 'C';
CREATE FUNCTION bar_list_finalfn(internal)
RETURNS text
AS 'vs_funcs', 'bar_list_finalfn'
LANGUAGE 'C';
CREATE AGGREGATE bar_list_agg (
BASETYPE = text, -- item type
SFUNC = bar_list_transfn, -- transition function
STYPE = internal, -- state type
FINALFUNC = bar_list_finalfn -- final calculation function
);
看起来您的SQL代码使用了错误的类型。从源代码(src/include/catalog/pg_proc.h
):
特别是:
... string_agg_transfn ... 2281 "2281 25 25" ...
... string_agg_finalfn ... 25 "2281" ...
... string_agg ... 25 "25 25" ...
第一个OID是返回类型。后面的OID是参数类型。可以通过查询目录对这些OID进行解码:
joey=# SELECT oid, typname FROM pg_type WHERE oid IN (25, 2281);
oid | typname
------+----------
25 | text
2281 | internal
(2 rows)
因此,正确的SQL语句应该是:
CREATE FUNCTION bar_list_transfn(internal, text)
RETURNS internal
AS 'vs_funcs', 'bar_list_transfn'
LANGUAGE 'C';
CREATE FUNCTION bar_list_finalfn(internal)
RETURNS text
AS 'vs_funcs', 'bar_list_finalfn'
LANGUAGE 'C';
CREATE AGGREGATE bar_list_agg (
BASETYPE = text, -- item type
SFUNC = bar_list_transfn, -- transition function
STYPE = internal, -- state type
FINALFUNC = bar_list_finalfn -- final calculation function
);
如果您只想在array_agg()周围创建一个包装器,那么完全不需要C
这个答案解释了如何使用纯SQL创建自己的聚合(而无需编写一行C代码)
不过,我不知道这是否符合您的“高性能”要求。如果您只想在array_agg()周围创建一个包装器,那么完全不需要C
这个答案解释了如何使用纯SQL创建自己的聚合(而无需编写一行C代码)
不过,我不知道这是否符合您的“高性能”。那个宏实在太大了。同意。然而,我真的不想复制和粘贴两次。那太傻了。那个宏实在太大了。我同意。然而,我真的不想复制和粘贴两次。那太傻了。这正是问题所在。谢谢这正是问题所在。谢谢我想包装string\u agg()
,这不是一回事。如果有一种简单的方法可以用两个参数包装聚合函数,这样就可以用一个参数调用它,请详细说明。这是必要的,因为它需要作为现有功能的替代品。我们已经在纯SQL中使用了聚合函数。它们很慢。我想包装string\u agg()
,这不是一回事。如果有一种简单的方法可以用两个参数包装聚合函数,这样就可以用一个参数调用它,请详细说明。这是必要的,因为它需要作为现有功能的替代品。我们已经在纯SQL中使用了聚合函数。他们很慢。