Describe the bug
UNWIND [null] may produce a row whose value prints as null, but count(x) still counts it as 1.
In Cypher, count(expr) should count only non-null values. After UNWIND [null] AS x, the single row has x = null, so count(x) should be 0.
Instead, Apache AGE returns 1.
How are you accessing AGE (Command line, driver, etc.)?
- PostgreSQL
cypher(...) wrapper through the local Python differential-testing harness
- Reproducible directly in
psql inside the Docker container
What data setup do we need to do?
No graph data is required beyond creating an empty graph:
SELECT create_graph('fuzz_graph');
What is the necessary configuration info needed?
- Plain Apache AGE Docker image was enough
- Docker image in local repro:
apache/age
- AGE extension version:
1.7.0
- PostgreSQL version:
18.1
- Graph name used in repro:
fuzz_graph
- No extra extensions or special configuration were required
What is the command that caused the error?
SELECT * FROM cypher('fuzz_graph', $$
UNWIND [null] AS x
RETURN count(x) AS c
$$) AS (c agtype);
Returned result on AGE:
Expected behavior
The result should be:
Neo4j returns 0 for the equivalent Cypher query:
UNWIND [null] AS x
RETURN count(x) AS c
Environment (please complete the following information):
- Version: Apache AGE
1.7.0
- PostgreSQL:
18.1
- Host OS: Windows
- Architecture: x86_64
- Deployment: Docker
Additional context
This appears to be a counting/null-semantics issue specifically around UNWIND, not a general count(null) problem.
Direct control cases on the same AGE instance:
- Plain
count(null) behaves correctly:
SELECT * FROM cypher('fuzz_graph', $$
RETURN count(null) AS c
$$) AS (c agtype);
Observed result:
WITH null AS x RETURN count(x) also behaves correctly:
SELECT * FROM cypher('fuzz_graph', $$
WITH null AS x
RETURN count(x) AS c
$$) AS (c agtype);
Observed result:
UNWIND [null] AS x RETURN x shows that the produced row is indeed null:
SELECT * FROM cypher('fuzz_graph', $$
UNWIND [null] AS x
RETURN x
$$) AS (x agtype);
Observed result:
A larger differential-testing case first exposed this via:
MATCH (p:Person)
OPTIONAL MATCH path = (p)-[:KNOWS]->(friend:Person)
WHERE p.age > 25
UNWIND [path] AS pth
RETURN p.name AS person_name, count(pth) AS friend_count
For rows where path was null, AGE returned friend_count = 1 instead of 0. Reducing the case to UNWIND [null] reproduced the same incorrect counting behavior.
Describe the bug
UNWIND [null]may produce a row whose value prints asnull, butcount(x)still counts it as1.In Cypher,
count(expr)should count only non-null values. AfterUNWIND [null] AS x, the single row hasx = null, socount(x)should be0.Instead, Apache AGE returns
1.How are you accessing AGE (Command line, driver, etc.)?
cypher(...)wrapper through the local Python differential-testing harnesspsqlinside the Docker containerWhat data setup do we need to do?
No graph data is required beyond creating an empty graph:
What is the necessary configuration info needed?
apache/age1.7.018.1fuzz_graphWhat is the command that caused the error?
Returned result on AGE:
Expected behavior
The result should be:
Neo4j returns
0for the equivalent Cypher query:Environment (please complete the following information):
1.7.018.1Additional context
This appears to be a counting/null-semantics issue specifically around
UNWIND, not a generalcount(null)problem.Direct control cases on the same AGE instance:
count(null)behaves correctly:Observed result:
WITH null AS x RETURN count(x)also behaves correctly:Observed result:
UNWIND [null] AS x RETURN xshows that the produced row is indeednull:Observed result:
A larger differential-testing case first exposed this via:
For rows where
pathwas null, AGE returnedfriend_count = 1instead of0. Reducing the case toUNWIND [null]reproduced the same incorrect counting behavior.