C语言 在数组中插入不同类型的值,将它们转换为错误的数据

mfpqipee  于 2023-08-03  发布在  其他
关注(0)|答案(3)|浏览(156)

我用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';


如果有人知道我该如何解决这个问题,我会很感激任何形式的帮助或建议

esyap4oy

esyap4oy1#

这不是一个解决方案,而是一个观察:
你的代码有一个问题,buffer正在被使用,每次你用它来解析浮点数/整数字符串时,它都被覆盖,它在同一个内存地址buffer上工作,那一行使它每次进入时都引用同一个地址,这样最新解析的值就被设置为所有旧的条目和那些被更改的条目

string_elem.val.string.val = gcvt(float_num, 6, buffer);

字符串
在您给出的示例中,所有设置为最后解析的数字,并限制字符串的长度,因为第二个元素是一个数字,所以它只采用最后解析的输入的第一个字符

lf3rwulv

lf3rwulv2#

我觉得问题出在这一行:string_elem.val.string.val = buffer;,循环的每次迭代都会覆盖缓冲区的内容。当您将string_elem.val.string.val赋值为buffer时,它不是复制数据,而是指向缓冲区的内存位置。

xytpbqjk

xytpbqjk3#

您正在为所有元素使用单个缓冲区,并且这以转换的元素的最后一个值结束。您需要为每个元素分配不同的内存才能使其成功。
您应该使用Postgres中的pstrdup函数来实现这一点。它确保所有元素都有单独的内存分配,并在不再有用时释放。
对于AGTV_STRING

string_elem.val.string.val = pstrdup(elem->val.string.val, elem->val.string.len);

字符串
对于AGTV_FLOAT

sprintf(buffer, sizeof(buffer), "%f", float_num);
string_elem.val.string.val = pstrdup(buffer);


对于AGTV_INTEGER

sprintf(buffer, sizeof(buffer), "%d", elem->val.int_value);
string_elem.val.string.val = pstrdup(buffer);


另外,对于FLOAT情况,我使用sprintf而不是gvct,因为它可以防止缓冲区溢出并更好地控制缓冲区大小。

相关问题