Skip to content

Commit

Permalink
added toFloatList() function (#1016)
Browse files Browse the repository at this point in the history
- Basically the same function of OpenCypher toFloatList() https://neo4j.com/docs/cypher-manual/current/functions/list/#functions-tofloatlist
- toFloatList() converts a list of values and returns a list of floating point values. If any values are not convertible to floating point they will be null in the list returned.
- Returns a list containing the converted elements; depending on the input value a converted value is either a floating point value or null.
  • Loading branch information
M4rcxs authored Jul 17, 2023
1 parent 4e26265 commit 256df9c
Show file tree
Hide file tree
Showing 4 changed files with 207 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 @@ -3514,6 +3514,14 @@ RETURNS NULL ON NULL INPUT
PARALLEL SAFE
AS 'MODULE_PATHNAME';

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

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

SELECT * FROM cypher('expr', $$
RETURN toFloatList([1.2, '4.654'])
$$) AS (toFloatList agtype);
tofloatlist
--------------
[1.2, 4.654]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toFloatList(['1.9432', 8.6222, '9.4111212', 344.22])
$$) AS (toFloatList agtype);
tofloatlist
-----------------------------------------
[1.9432, 8.6222, 9.4111212, 344.220001]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toFloatList(['999.2'])
$$) AS (toFloatList agtype);
tofloatlist
-------------
[999.2]
(1 row)

SELECT * FROM cypher('expr', $$
RETURN toFloatList([1.20002])
$$) AS (toFloatList agtype);
tofloatlist
-------------
[1.20002]
(1 row)

-- should return null
SELECT * FROM cypher('expr', $$
RETURN toFloatList(['true'])
$$) AS (toFloatList agtype);
tofloatlist
-------------
[null]
(1 row)

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

-- should fail
SELECT * FROM cypher('expr', $$
RETURN toFloatList([failed])
$$) AS (toFloatList agtype);
ERROR: could not find rte for failed
LINE 2: RETURN toFloatList([failed])
^
SELECT * FROM cypher('expr', $$
RETURN toFloatList("failed")
$$) AS (toFloatList agtype);
ERROR: toFloatList() argument must resolve to a list or null
SELECT * FROM cypher('expr', $$
RETURN toFloatList(555)
$$) AS (toFloatList agtype);
ERROR: toFloatList() argument must resolve to a list or null
-- toInteger()
SELECT * FROM cypher('expr', $$
RETURN toInteger(1)
Expand Down
33 changes: 33 additions & 0 deletions regress/sql/expr.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,39 @@ $$) AS (toFloat agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloat()
$$) AS (toFloat agtype);
-- toFloatList()
SELECT * FROM cypher('expr', $$
RETURN toFloatList([1.3])
$$) AS (toFloatList agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloatList([1.2, '4.654'])
$$) AS (toFloatList agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloatList(['1.9432', 8.6222, '9.4111212', 344.22])
$$) AS (toFloatList agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloatList(['999.2'])
$$) AS (toFloatList agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloatList([1.20002])
$$) AS (toFloatList agtype);
-- should return null
SELECT * FROM cypher('expr', $$
RETURN toFloatList(['true'])
$$) AS (toFloatList agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloatList([null])
$$) AS (toFloatList agtype);
-- should fail
SELECT * FROM cypher('expr', $$
RETURN toFloatList([failed])
$$) AS (toFloatList agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloatList("failed")
$$) AS (toFloatList agtype);
SELECT * FROM cypher('expr', $$
RETURN toFloatList(555)
$$) AS (toFloatList agtype);
-- toInteger()
SELECT * FROM cypher('expr', $$
RETURN toInteger(1)
Expand Down
93 changes: 93 additions & 0 deletions src/backend/utils/adt/agtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -5316,6 +5316,99 @@ Datum age_tofloat(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
}

PG_FUNCTION_INFO_V1(age_tofloatlist);
/*
* toFloatList() converts a list of values and returns a list of floating point values.
* If any values are not convertible to floating point they will be null in the list returned.
*/
Datum age_tofloatlist(PG_FUNCTION_ARGS)
{
agtype *agt_arg = NULL;
agtype_in_state agis_result;
agtype_value *elem;
agtype_value float_elem;
char *string = NULL;
int count;
int i;
bool is_valid = false;
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("toFloatList() 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 float if possible.
elem = get_ith_agtype_value_from_container(&agt_arg->root, i);
float_elem.type = AGTV_FLOAT;

switch (elem->type)
{
case AGTV_STRING:

string = elem->val.string.val;
if (atof(string))
{
float_elem.type = AGTV_FLOAT;
float_elem.val.float_value = float8in_internal_null(string, NULL, "double precision",
string, &is_valid);
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &float_elem);
}
else
{
float_elem.type = AGTV_NULL;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &float_elem);
}

break;

case AGTV_FLOAT:

float_elem.type = AGTV_FLOAT;
float_num = elem->val.float_value;
sprintf(buffer, "%f", float_num);
string = buffer;
float_elem.val.float_value = float8in_internal_null(string, NULL, "double precision", string, &is_valid);
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &float_elem);

break;

default:

float_elem.type = AGTV_NULL;
agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, &float_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_tointeger);

Datum age_tointeger(PG_FUNCTION_ARGS)
Expand Down

0 comments on commit 256df9c

Please sign in to comment.