Skip to content

Commit

Permalink
Implemented the toBooleanList() function (#1014)
Browse files Browse the repository at this point in the history
- Essentially the same as openCypher's toBooleanList() function: https://neo4j.com/docs/cypher-manual/current/functions/list/#functions-tobooleanlist
- The toBooleanList() function converts a list of values and returns a list of boolean values.
- If any values are not convertible to boolean they will be null in the list returned.
- Also created the regression tests for it.
  • Loading branch information
MatheusFarias03 authored and jrgemignani committed Dec 13, 2023
1 parent 92f972f commit def28f9
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 0 deletions.
8 changes: 8 additions & 0 deletions age--1.4.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3507,6 +3507,14 @@ RETURNS NULL ON NULL INPUT
PARALLEL SAFE
AS 'MODULE_PATHNAME';

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

CREATE FUNCTION ag_catalog.age_tofloat(variadic "any")
RETURNS agtype
LANGUAGE c
Expand Down
81 changes: 81 additions & 0 deletions regress/expected/expr.out
Original file line number Diff line number Diff line change
Expand Up @@ -2827,6 +2827,87 @@ ERROR: function ag_catalog.age_toboolean() does not exist
LINE 2: RETURN toBoolean()
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
-- toBooleanList()
SELECT * FROM cypher('expr', $$
RETURN toBooleanList([true, false, true])
$$) AS (toBooleanList agtype);
tobooleanlist
---------------------
[true, false, true]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toBooleanList(["true", "false", "true"])
$$) AS (toBooleanList agtype);
tobooleanlist
---------------------
[true, false, true]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toBooleanList(["True", "False", "True"])
$$) AS (toBooleanList agtype);
tobooleanlist
---------------------
[true, false, true]
(1 row)

-- should return null
SELECT * FROM cypher('expr', $$
RETURN toBooleanList([])
$$) AS (toBooleanList agtype);
tobooleanlist
---------------

(1 row)

SELECT * FROM cypher('expr', $$
RETURN toBooleanList([null, null, null])
$$) AS (toBooleanList agtype);
tobooleanlist
--------------------
[null, null, null]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toBooleanList(["Hello", "world!"])
$$) AS (toBooleanList agtype);
tobooleanlist
---------------
[null, null]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toBooleanList([["A", "B"], ["C", "D"]])
$$) AS (toBooleanList agtype);
tobooleanlist
---------------
[null, null]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toBooleanList([0,1,2,3,4])
$$) AS (toBooleanList agtype);
tobooleanlist
--------------------------------
[null, null, null, null, null]
(1 row)

-- should fail
SELECT * FROM cypher('expr', $$
RETURN toBooleanList(fail)
$$) AS (toBooleanList agtype);
ERROR: could not find rte for fail
LINE 2: RETURN toBooleanList(fail)
^
SELECT * FROM cypher('expr', $$
RETURN toBooleanList("fail")
$$) AS (toBooleanList agtype);
ERROR: toBooleanList() argument must resolve to a list or null
SELECT * FROM cypher('expr', $$
RETURN toBooleanList(123)
$$) AS (toBooleanList agtype);
ERROR: toBooleanList() argument must resolve to a list or null
-- toFloat()
SELECT * FROM cypher('expr', $$
RETURN toFloat(1)
Expand Down
48 changes: 48 additions & 0 deletions regress/sql/expr.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,54 @@ $$) AS (toBoolean agtype);
SELECT * FROM cypher('expr', $$
RETURN toBoolean()
$$) AS (toBoolean agtype);

-- toBooleanList()
SELECT * FROM cypher('expr', $$
RETURN toBooleanList([true, false, true])
$$) AS (toBooleanList agtype);

SELECT * FROM cypher('expr', $$
RETURN toBooleanList(["true", "false", "true"])
$$) AS (toBooleanList agtype);

SELECT * FROM cypher('expr', $$
RETURN toBooleanList(["True", "False", "True"])
$$) AS (toBooleanList agtype);

-- should return null
SELECT * FROM cypher('expr', $$
RETURN toBooleanList([])
$$) AS (toBooleanList agtype);

SELECT * FROM cypher('expr', $$
RETURN toBooleanList([null, null, null])
$$) AS (toBooleanList agtype);

SELECT * FROM cypher('expr', $$
RETURN toBooleanList(["Hello", "world!"])
$$) AS (toBooleanList agtype);

SELECT * FROM cypher('expr', $$
RETURN toBooleanList([["A", "B"], ["C", "D"]])
$$) AS (toBooleanList agtype);

SELECT * FROM cypher('expr', $$
RETURN toBooleanList([0,1,2,3,4])
$$) AS (toBooleanList agtype);

-- should fail
SELECT * FROM cypher('expr', $$
RETURN toBooleanList(fail)
$$) AS (toBooleanList agtype);

SELECT * FROM cypher('expr', $$
RETURN toBooleanList("fail")
$$) AS (toBooleanList agtype);

SELECT * FROM cypher('expr', $$
RETURN toBooleanList(123)
$$) AS (toBooleanList agtype);

-- toFloat()
SELECT * FROM cypher('expr', $$
RETURN toFloat(1)
Expand Down
92 changes: 92 additions & 0 deletions src/backend/utils/adt/agtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -5199,6 +5199,98 @@ Datum age_toboolean(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
}

PG_FUNCTION_INFO_V1(age_tobooleanlist);
/*
* Converts a list of values and returns a list of boolean values.
* If any values are not convertible to boolean they will be null in the list returned.
*/
Datum age_tobooleanlist(PG_FUNCTION_ARGS)
{
agtype *agt_arg = NULL;
agtype_in_state agis_result;
agtype_value *elem;
agtype_value bool_elem;
char *string = NULL;
int count;
int i;

/* 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("toBooleanList() 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 boolean if possible.
elem = get_ith_agtype_value_from_container(&agt_arg->root, i);
bool_elem.type = AGTV_BOOL;

switch (elem->type)
{
case AGTV_STRING:

string = elem->val.string.val;

if (pg_strcasecmp(string, "true") == 0)
{
bool_elem.val.boolean = true;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &bool_elem);
}
else if (pg_strcasecmp(string, "false") == 0)
{
bool_elem.val.boolean = false;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &bool_elem);
}
else
{
bool_elem.type = AGTV_NULL;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &bool_elem);
}

break;

case AGTV_BOOL:

bool_elem.val.boolean = elem->val.boolean;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &bool_elem);

break;

default:

bool_elem.type = AGTV_NULL;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &bool_elem);

break;
}
}

/* push the end of the array */
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_tofloat);

Datum age_tofloat(PG_FUNCTION_ARGS)
Expand Down

0 comments on commit def28f9

Please sign in to comment.