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));

Reply via email to