This is an automated email from the ASF dual-hosted git repository.
dehowef pushed a commit to branch PG12
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/PG12 by this push:
new 058235ee add toIntegerList() function (#1058)
058235ee is described below
commit 058235ee7d947138e957da90c29e14ba0c8cd017
Author: Marcos Silva <[email protected]>
AuthorDate: Tue Jul 18 18:50:57 2023 -0300
add toIntegerList() function (#1058)
- This function is inspired by
https://neo4j.com/docs/cypher-manual/current/functions/list/#functions-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.
---
age--1.3.0.sql | 8 +++
regress/expected/expr.out | 69 ++++++++++++++++++++++++
regress/sql/expr.sql | 30 +++++++++++
src/backend/utils/adt/agtype.c | 118 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 225 insertions(+)
diff --git a/age--1.3.0.sql b/age--1.3.0.sql
index 1f6a8acf..3a257820 100644
--- a/age--1.3.0.sql
+++ b/age--1.3.0.sql
@@ -3529,6 +3529,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
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index cc1ed8e2..612b5697 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -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)
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 744a071f..a241b0b4 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -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)
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 6ffce718..7428c349 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -5599,6 +5599,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)