我用C语言创建了一个AGE函数,它将一个元素列表转换为一个新的字符串元素列表,基于opencypher的toStringList。
当我在AGE中的C中的数组中插入不同的数据类型时,返回的数组将原始数组中的元素修改为等于最后一个元素:
demo=# SELECT * FROM cypher('grafo', $$
RETURN toStringList([1.3, 8, 7.4, 2.5])
$$) AS (toFloatList agtype);
tofloatlist
----------------------------
["2.5", "2", "2.5", "2.5"]
(1 row)
字符串
toStringList()函数:
PG_FUNCTION_INFO_V1(age_tostringlist);
/*
* toStringList() converts a list of values and returns a list of String values.
* If any values are not convertible to string point they will be null in the list returned.
*/
Datum age_tostringlist(PG_FUNCTION_ARGS)
{
agtype *agt_arg = NULL;
agtype_in_state agis_result;
agtype_value *elem;
agtype_value string_elem;
char *string = NULL;
int count;
int i;
float float_num;
char buffer[64];
/* check for null */
if (PG_ARGISNULL(0))
{
PG_RETURN_NULL();
}
agt_arg = AG_GET_ARG_AGTYPE_P(0);
/* check for an array */
if (!AGT_ROOT_IS_ARRAY(agt_arg) || AGT_ROOT_IS_SCALAR(agt_arg))
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("toStringList() argument must resolve to a list or null")));
count = AGT_ROOT_COUNT(agt_arg);
/* if we have an empty list or only one element in the list, return null */
if (count == 0)
PG_RETURN_NULL();
/* clear the result structure */
MemSet(&agis_result, 0, sizeof(agtype_in_state));
/* push the beginning of the array */
agis_result.res = push_agtype_value(&agis_result.parse_state,
WAGT_BEGIN_ARRAY, NULL);
/* iterate through the list */
for (i = 0; i < count; i++)
{
// TODO: check element's type, it's value, and convert it to string if possible.
elem = get_ith_agtype_value_from_container(&agt_arg->root, i);
string_elem.type = AGTV_STRING;
switch (elem->type)
{
case AGTV_STRING:
if(!elem)
{
string_elem.type = AGTV_NULL;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &string_elem);
}
string_elem.val.string.val = elem->val.string.val;
string_elem.val.string.len = elem->val.string.len;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &string_elem);
break;
case AGTV_FLOAT:
string_elem.type = AGTV_STRING;
// sprintf(buffer, "%d", elem->val.float_value);
float_num = elem->val.float_value;
string_elem.val.string.val = gcvt(float_num, 6, buffer);
string_elem.val.string.len = strlen(buffer);
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &string_elem);
break;
case AGTV_INTEGER:
string_elem.type = AGTV_STRING;
sprintf(buffer, "%d", elem->val.int_value);
string_elem.val.string.val = buffer;
string_elem.val.string.len = strlen(buffer);
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &string_elem);
break;
default:
string_elem.type = AGTV_NULL;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &string_elem);
break;
}
}
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_END_ARRAY, NULL);
PG_RETURN_POINTER(agtype_value_to_agtype(agis_result.res));
}
型
age--1.3.0.sql文件
CREATE FUNCTION ag_catalog.age_tostringlist(variadic "any")
RETURNS agtype
LANGUAGE c
IMMUTABLE
RETURNS NULL ON NULL INPUT
PARALLEL SAFE
AS 'MODULE_PATHNAME';
型
如果有人知道我该如何解决这个问题,我会很感激任何形式的帮助或建议
3条答案
按热度按时间esyap4oy1#
这不是一个解决方案,而是一个观察:
你的代码有一个问题,buffer正在被使用,每次你用它来解析浮点数/整数字符串时,它都被覆盖,它在同一个内存地址buffer上工作,那一行使它每次进入时都引用同一个地址,这样最新解析的值就被设置为所有旧的条目和那些被更改的条目
字符串
在您给出的示例中,所有设置为最后解析的数字,并限制字符串的长度,因为第二个元素是一个数字,所以它只采用最后解析的输入的第一个字符
lf3rwulv2#
我觉得问题出在这一行:
string_elem.val.string.val = buffer;
,循环的每次迭代都会覆盖缓冲区的内容。当您将string_elem.val.string.val
赋值为buffer
时,它不是复制数据,而是指向缓冲区的内存位置。xytpbqjk3#
您正在为所有元素使用单个缓冲区,并且这以转换的元素的最后一个值结束。您需要为每个元素分配不同的内存才能使其成功。
您应该使用Postgres中的
pstrdup
函数来实现这一点。它确保所有元素都有单独的内存分配,并在不再有用时释放。对于AGTV_STRING:
字符串
对于AGTV_FLOAT:
型
对于AGTV_INTEGER:
型
另外,对于FLOAT情况,我使用
sprintf
而不是gvct
,因为它可以防止缓冲区溢出并更好地控制缓冲区大小。