From bc935c039107194da01c1377f6d7d9edd58ddef9 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 16 Oct 2023 17:46:41 -0300 Subject: [PATCH] Implement age_tail function 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 4d8c9f048..e220d7f63 100644 --- a/age--1.4.0.sql +++ b/age--1.4.0.sql @@ -3562,6 +3562,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 c52056e7f..24ae79340 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -6709,6 +6709,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 779623a98..62d7d3eb2 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -2730,6 +2730,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 639157f68..01d7312ef 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -5007,6 +5007,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)