From d48c008a2b1886ae5765d6f306911b9abe80c425 Mon Sep 17 00:00:00 2001 From: Matheus Farias de Oliveira Matsumoto <83461020+MatheusFarias03@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:07:56 -0300 Subject: [PATCH] Implemented age_tail function (#1283) age_tail() function returns a list containing all the elements, excluding the first one, from a list. --- age--1.4.0.sql | 8 ++++ regress/expected/expr.out | 39 +++++++++++++++++ regress/sql/expr.sql | 11 +++++ src/backend/utils/adt/agtype.c | 77 ++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) diff --git a/age--1.4.0.sql b/age--1.4.0.sql index 47e8163c6..caebb22c4 100644 --- a/age--1.4.0.sql +++ b/age--1.4.0.sql @@ -3590,6 +3590,14 @@ RETURNS NULL ON NULL INPUT PARALLEL SAFE AS 'MODULE_PATHNAME'; +CREATE FUNCTION ag_catalog.age_tail(variadic "any") +RETURNS agtype +LANGUAGE c +IMMUTABLE +RETURNS NULL ON NULL INPUT +PARALLEL SAFE +AS 'MODULE_PATHNAME'; + CREATE FUNCTION ag_catalog.age_properties(agtype) RETURNS agtype LANGUAGE c diff --git a/regress/expected/expr.out b/regress/expected/expr.out index d85b51de7..8c1a26cd4 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -6928,6 +6928,45 @@ SELECT * from cypher('list', $$RETURN range(0, null, -3)$$) as (range agtype); ERROR: range(): neither start or end can be NULL SELECT * from cypher('list', $$RETURN range(0, -10.0, -3.0)$$) as (range agtype); ERROR: range() unsupported argument type +-- tail() +-- should return the last elements of the list +SELECT * FROM cypher('list', $$ RETURN tail([1,2,3,4,5]) $$) AS (tail agtype); + tail +-------------- + [2, 3, 4, 5] +(1 row) + +SELECT * FROM cypher('list', $$ RETURN tail(["a","b","c","d","e"]) $$) AS (tail agtype); + tail +---------------------- + ["b", "c", "d", "e"] +(1 row) + +-- should return null +SELECT * FROM cypher('list', $$ RETURN tail([1]) $$) AS (tail agtype); + tail +------ + +(1 row) + +SELECT * FROM cypher('list', $$ RETURN tail([]) $$) AS (tail agtype); + tail +------ + +(1 row) + +-- should throw errors +SELECT * FROM cypher('list', $$ RETURN tail(123) $$) AS (tail agtype); +ERROR: tail() argument must resolve to a list or null +SELECT * FROM cypher('list', $$ RETURN tail(abc) $$) AS (tail agtype); +ERROR: could not find rte for abc +LINE 1: SELECT * FROM cypher('list', $$ RETURN tail(abc) $$) AS (tai... + ^ +SELECT * FROM cypher('list', $$ RETURN tail() $$) AS (tail agtype); +ERROR: function ag_catalog.age_tail() does not exist +LINE 1: SELECT * FROM cypher('list', $$ RETURN tail() $$) AS (tail a... + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. -- labels() SELECT * from cypher('list', $$CREATE (u:People {name: "John"}) RETURN u$$) as (Vertices agtype); vertices diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql index 26acc1b38..13d1ca3c3 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -2830,6 +2830,17 @@ SELECT * from cypher('list', $$RETURN range(-10, 10, -1)$$) as (range agtype); SELECT * from cypher('list', $$RETURN range(null, -10, -3)$$) as (range agtype); SELECT * from cypher('list', $$RETURN range(0, null, -3)$$) as (range agtype); SELECT * from cypher('list', $$RETURN range(0, -10.0, -3.0)$$) as (range agtype); +-- tail() +-- should return the last elements of the list +SELECT * FROM cypher('list', $$ RETURN tail([1,2,3,4,5]) $$) AS (tail agtype); +SELECT * FROM cypher('list', $$ RETURN tail(["a","b","c","d","e"]) $$) AS (tail agtype); +-- should return null +SELECT * FROM cypher('list', $$ RETURN tail([1]) $$) AS (tail agtype); +SELECT * FROM cypher('list', $$ RETURN tail([]) $$) AS (tail agtype); +-- should throw errors +SELECT * FROM cypher('list', $$ RETURN tail(123) $$) AS (tail agtype); +SELECT * FROM cypher('list', $$ RETURN tail(abc) $$) AS (tail agtype); +SELECT * FROM cypher('list', $$ RETURN tail() $$) AS (tail agtype); -- labels() SELECT * from cypher('list', $$CREATE (u:People {name: "John"}) RETURN u$$) as (Vertices agtype); SELECT * from cypher('list', $$CREATE (u:People {name: "Larry"}) RETURN u$$) as (Vertices agtype); diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c index 84eed5924..e492a38b8 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -5126,6 +5126,83 @@ Datum age_last(PG_FUNCTION_ARGS) PG_RETURN_POINTER(agtype_value_to_agtype(agtv_result)); } + +PG_FUNCTION_INFO_V1(age_tail); +/* + * Returns a list containing all the elements, excluding the first one, from a list. + */ +Datum age_tail(PG_FUNCTION_ARGS) +{ + Oid arg_type; + agtype *agt_arg = NULL; + agtype *agt_result = NULL; + agtype_in_state agis_result; + int count; + int i; + + /* check number of arguments */ + if (PG_NARGS() < 1 || PG_NARGS() > 1) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("age_tail() requires only one argument"))); + } + + /* get the data type */ + arg_type = get_fn_expr_argtype(fcinfo->flinfo, 0); + + /* check the data type */ + if (arg_type != AGTYPEOID) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("age_tail() argument must be of type agtype"))); + } + + /* 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("tail() 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 <= 1) + { + 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 beginning with the second item */ + for (i = 1; i < count; i++) + { + agis_result.res = push_agtype_value(&agis_result.parse_state, WAGT_ELEM, + get_ith_agtype_value_from_container(&agt_arg->root, i)); + } + + /* push the end of the array */ + agis_result.res = push_agtype_value(&agis_result.parse_state, + WAGT_END_ARRAY, NULL); + + agt_result = agtype_value_to_agtype(agis_result.res); + pfree_agtype_value(agis_result.res); + + PG_RETURN_POINTER(agt_result); +} + PG_FUNCTION_INFO_V1(age_properties); Datum age_properties(PG_FUNCTION_ARGS)