shulei5831sl opened a new issue, #2472:
URL: https://github.com/apache/age/issues/2472
### Apache AGE version
Observed on Docker image:
```text
docker pull apache/age:latest
Pulled: 2026-07-09
Apache AGE: PG18
Endpoint:
postgres://127.0.0.1:5432
```
### How are you accessing AGE
psql and Python `psycopg2` driver
### Environment
* Host OS: Linux `5.4.0-216-generic`
* Deployment: Docker `apache/age:latest`
* Query interface: psql and psycopg2
* Query language: Cypher via `ag_catalog.cypher`
* Configuration: default AGE on PostgreSQL 18
* Differential comparison targets:
* Neo4j `2026.05.0`
### Description
AGE's `tail()` function returns `null` when the result should be an empty
list.
The issue occurs for:
```cypher
tail([1])
tail([])
```
Both should return `[]`.
`tail()` is a list-returning function: it returns all elements of the input
list except the first one. Therefore, dropping the only element from `[1]`
should produce an empty list, and applying `tail()` to an already empty list
should also produce an empty list.
Neo4j returns `[]` for both cases.
### Setup
```pgsql
LOAD 'age';
SET search_path = ag_catalog, '$user', public;
SELECT * FROM ag_catalog.create_graph('test_graph');
```
### Failing queries
```pgsql
SELECT * FROM cypher('test_graph', $$
RETURN tail([1]) AS r
$$) AS (r agtype);
```
AGE returns:
```text
r = null
```
Expected:
```text
r = []
```
```pgsql
SELECT * FROM cypher('test_graph', $$
RETURN tail([]) AS r
$$) AS (r agtype);
```
AGE returns:
```text
r = null
```
Expected:
```text
r = []
```
### Control queries
#### Normal `tail()` works
```pgsql
SELECT * FROM cypher('test_graph', $$
RETURN tail([1,2,3]) AS r
$$) AS (r agtype);
```
AGE returns:
```text
r = [2, 3]
```
This is correct.
#### `tail(null)` should return `null`
```pgsql
SELECT * FROM cypher('test_graph', $$
RETURN tail(null) AS r
$$) AS (r agtype);
```
Expected:
```text
r = null
```
This distinguishes null input from empty-list output.
#### Other list functions show the expected boundary behavior
```pgsql
SELECT * FROM cypher('test_graph', $$
RETURN reverse([]) AS r
$$) AS (r agtype);
```
AGE returns:
```text
r = []
```
This is correct: a list-returning function applied to an empty list returns
an empty list.
```pgsql
SELECT * FROM cypher('test_graph', $$
RETURN head([]) AS r
$$) AS (r agtype);
```
AGE returns:
```text
r = null
```
This is correct: `head()` returns an element, and there is no element in an
empty list.
```pgsql
SELECT * FROM cypher('test_graph', $$
RETURN last([]) AS r
$$) AS (r agtype);
```
AGE returns:
```text
r = null
```
This is correct: `last()` returns an element, and there is no element in an
empty list.
### Cross-engine comparison
| Expression | Apache AGE | Neo4j `2026.05.0` |
| --------------- | ---------: | ----------------: |
| `tail([1])` | `null` | `[]` |
| `tail([])` | `null` | `[]` |
| `tail([1,2,3])` | `[2,3]` | `[2,3]` |
| `tail(null)` | `null` | `null` |
| `reverse([])` | `[]` | `[]` |
| `head([])` | `null` | `null` |
| `last([])` | `null` | `null` |
### Impact
This can silently change query logic when `tail()` is used in list pipelines.
For example:
```pgsql
SELECT * FROM cypher('test_graph', $$
RETURN size(tail([1])) AS r
$$) AS (r agtype);
```
Expected:
```text
r = 0
```
AGE may return `null` because `tail([1])` returns `null`.
This also affects predicates and CASE expressions that distinguish an empty
list from `null`.
### Summary
`tail()` should return an empty list when all elements are removed from the
input list. AGE currently returns `null` for `tail([1])` and `tail([])`, even
though it correctly returns lists for non-empty outputs such as `tail([1,2,3])`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]