This is an automated email from the ASF dual-hosted git repository.
MuhammadTahaNaveed pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/master by this push:
new 908d7b29 Return an empty list from tail() for empty and singleton
lists (#2399)
908d7b29 is described below
commit 908d7b29811a23d7b01c024af5d6697fe670e123
Author: Sai Asish Y <[email protected]>
AuthorDate: Mon Apr 20 02:32:54 2026 -0700
Return an empty list from tail() for empty and singleton lists (#2399)
age_tail() in src/backend/utils/adt/agtype.c had an explicit early return
that mapped both the empty-list and the singleton-list cases to SQL 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();
}
The Cypher specification (and Neo4j / Memgraph) both
define tail(list) as "the list without its first element". For a
zero-or-one-element input that is an empty list [], not null. Differential
tests against Neo4j surface this divergence immediately.
Remove the early return. The loop below already iterates from i=1 so it
naturally emits [] for count <= 1 and emits the correct tail for the
general case. No behaviour change for count >= 2. Tests and expected
output are updated to match the new, spec-correct result.
---
regress/expected/expr.out | 6 +++---
regress/sql/expr.sql | 2 +-
src/backend/utils/adt/agtype.c | 10 +++++-----
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 6d934145..e95fe14e 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -8039,17 +8039,17 @@ SELECT * FROM cypher('list', $$ RETURN
tail(["a","b","c","d","e"]) $$) AS (tail
["b", "c", "d", "e"]
(1 row)
--- should return null
+-- should return an empty list
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
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 445e2d23..7304d4d6 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -3269,7 +3269,7 @@ SELECT * from cypher('list', $$RETURN range(0, -10.0,
-3.0)$$) as (range agtype)
-- 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
+-- should return an empty list
SELECT * FROM cypher('list', $$ RETURN tail([1]) $$) AS (tail agtype);
SELECT * FROM cypher('list', $$ RETURN tail([]) $$) AS (tail agtype);
-- should throw errors
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index b2215d01..8fad00bd 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -6056,11 +6056,11 @@ Datum age_tail(PG_FUNCTION_ARGS)
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();
- }
+ /*
+ * For an empty or singleton list, tail() returns an empty list. The loop
+ * below already produces that result (i starts at 1 so nothing is pushed
+ * when count <= 1), so we do not special-case the count here.
+ */
/* clear the result structure */
MemSet(&agis_result, 0, sizeof(agtype_in_state));