Skip to content

Commit

Permalink
added toIntegerList() function (#1025)
Browse files Browse the repository at this point in the history
- This function is inspired by toIntegerList() in OpenCypher.
- The function toIntegerList() takes a list of values and produces a new list containing only the integer values. If any values cannot be converted to an integer, they will be represented as null in the returned list.
- The result of the toIntegerList() function is a list that contains the converted elements. The converted values in the list can be either integer values or null, depending on the input value.
- Also added the regression tests.
  • Loading branch information
M4rcxs authored Jul 17, 2023
1 parent 256df9c commit f41f6da
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 0 deletions.
8 changes: 8 additions & 0 deletions age--1.3.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3530,6 +3530,14 @@ RETURNS NULL ON NULL INPUT
PARALLEL SAFE
AS 'MODULE_PATHNAME';

CREATE FUNCTION ag_catalog.age_tointegerlist(variadic "any")
RETURNS agtype
LANGUAGE c
IMMUTABLE
RETURNS NULL ON NULL INPUT
PARALLEL SAFE
AS 'MODULE_PATHNAME';

CREATE FUNCTION ag_catalog.age_tostring(variadic "any")
RETURNS agtype
LANGUAGE c
Expand Down
69 changes: 69 additions & 0 deletions regress/expected/expr.out
Original file line number Diff line number Diff line change
Expand Up @@ -3032,6 +3032,75 @@ ERROR: function ag_catalog.age_tointeger() does not exist
LINE 2: RETURN toInteger()
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
-- toIntegerList()
SELECT * FROM cypher('expr', $$
RETURN toIntegerList([1, 7.8, 9.0, '88'])
$$) AS (toIntegerList agtype);
tointegerlist
---------------
[1, 7, 9, 88]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toIntegerList([4.2, '123', '8', 8])
$$) AS (toIntegerList agtype);
tointegerlist
----------------
[4, 123, 8, 8]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toIntegerList(['41', '12', 2])
$$) AS (toIntegerList agtype);
tointegerlist
---------------
[41, 12, 2]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toIntegerList([1, 2, 3, '10.2'])
$$) AS (toIntegerList agtype);
tointegerlist
---------------
[1, 2, 3, 10]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toIntegerList([0000])
$$) AS (toIntegerList agtype);
tointegerlist
---------------
[0]
(1 row)

-- should return null
SELECT * FROM cypher('expr', $$
RETURN toIntegerList(["false_", 'asdsad', '123k1kdk1'])
$$) AS (toIntegerList agtype);
tointegerlist
--------------------
[null, null, null]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toIntegerList([null, '123false', 'one'])
$$) AS (toIntegerList agtype);
tointegerlist
--------------------
[null, null, null]
(1 row)

-- should fail
SELECT * FROM cypher('expr', $$
RETURN toIntegerList(123, '123')
$$) AS (toIntegerList agtype);
ERROR: toIntegerList() argument must resolve to a list or null
SELECT * FROM cypher('expr', $$
RETURN toIntegerList(32[])
$$) AS (toIntegerList agtype);
ERROR: syntax error at or near "]"
LINE 2: RETURN toIntegerList(32[])
^
-- length() of a path
SELECT * FROM cypher('expr', $$
RETURN length([{id: 0, label: "vertex 0", properties: {}}::vertex, {id: 2, label: "edge 0", end_id: 1, start_id: 0, properties: {}}::edge, {id: 1, label: "vertex 1", properties: {}}::vertex]::path)
Expand Down
30 changes: 30 additions & 0 deletions regress/sql/expr.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1362,6 +1362,36 @@ $$) AS (toInteger agtype);
SELECT * FROM cypher('expr', $$
RETURN toInteger()
$$) AS (toInteger agtype);
-- toIntegerList()
SELECT * FROM cypher('expr', $$
RETURN toIntegerList([1, 7.8, 9.0, '88'])
$$) AS (toIntegerList agtype);
SELECT * FROM cypher('expr', $$
RETURN toIntegerList([4.2, '123', '8', 8])
$$) AS (toIntegerList agtype);
SELECT * FROM cypher('expr', $$
RETURN toIntegerList(['41', '12', 2])
$$) AS (toIntegerList agtype);
SELECT * FROM cypher('expr', $$
RETURN toIntegerList([1, 2, 3, '10.2'])
$$) AS (toIntegerList agtype);
SELECT * FROM cypher('expr', $$
RETURN toIntegerList([0000])
$$) AS (toIntegerList agtype);
-- should return null
SELECT * FROM cypher('expr', $$
RETURN toIntegerList(["false_", 'asdsad', '123k1kdk1'])
$$) AS (toIntegerList agtype);
SELECT * FROM cypher('expr', $$
RETURN toIntegerList([null, '123false', 'one'])
$$) AS (toIntegerList agtype);
-- should fail
SELECT * FROM cypher('expr', $$
RETURN toIntegerList(123, '123')
$$) AS (toIntegerList agtype);
SELECT * FROM cypher('expr', $$
RETURN toIntegerList(32[])
$$) AS (toIntegerList agtype);
-- length() of a path
SELECT * FROM cypher('expr', $$
RETURN length([{id: 0, label: "vertex 0", properties: {}}::vertex, {id: 2, label: "edge 0", end_id: 1, start_id: 0, properties: {}}::edge, {id: 1, label: "vertex 1", properties: {}}::vertex]::path)
Expand Down
118 changes: 118 additions & 0 deletions src/backend/utils/adt/agtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -5603,6 +5603,124 @@ Datum age_tointeger(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
}

PG_FUNCTION_INFO_V1(age_tointegerlist);
/*
* toIntegerList() converts a list of values and returns a list of integers point values.
* If any values are not convertible to integer they will be null in the list returned.
*/
Datum age_tointegerlist(PG_FUNCTION_ARGS)
{
agtype *agt_arg = NULL;
agtype_in_state agis_result;
agtype_value *elem;
agtype_value integer_elem;
int count;
int i;
char *string = NULL;
int integer_num;
float float_num;
int is_float;

/* 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("toIntegerList() 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 integer if possible.
elem = get_ith_agtype_value_from_container(&agt_arg->root, i);
integer_elem.type = AGTV_INTEGER;

switch (elem->type)
{
case AGTV_STRING:

string = elem->val.string.val;
integer_elem.type = AGTV_INTEGER;
integer_elem.val.int_value = atoi(string);

if (*string == '+' || *string == '-' || (*string >= '0' && *string <= '9'))
{
is_float = 1;
while (*(++string))
{

if(!(*string >= '0' && *string <= '9'))
{
if(*string == '.' && is_float)
{
is_float--;
}
else
{
integer_elem.type = AGTV_NULL;
break;
}
}
}
}
else
{

integer_elem.type = AGTV_NULL;
}

agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &integer_elem);

break;

case AGTV_FLOAT:

integer_elem.type = AGTV_INTEGER;
float_num = elem->val.float_value;
integer_elem.val.int_value = (int)float_num;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &integer_elem);

break;

case AGTV_INTEGER:

integer_elem.type = AGTV_INTEGER;
integer_num = elem->val.int_value;
integer_elem.val.int_value = integer_num;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &integer_elem);

break;

default:

integer_elem.type = AGTV_NULL;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &integer_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));
}

PG_FUNCTION_INFO_V1(age_size);

Datum age_size(PG_FUNCTION_ARGS)
Expand Down

0 comments on commit f41f6da

Please sign in to comment.