diff --git a/age--1.3.0.sql b/age--1.3.0.sql index 6d8730f9b..0f04a5f11 100644 --- a/age--1.3.0.sql +++ b/age--1.3.0.sql @@ -3503,6 +3503,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 diff --git a/regress/expected/expr.out b/regress/expected/expr.out index 1dfcba9db..ca35bbde1 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -2818,6 +2818,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) diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql index 186cc6fc6..e5a7b2e6a 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -1268,6 +1268,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) diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c index c8a88d9ee..0bee39609 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -5144,6 +5144,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)