Skip to content

UNWIND [null] may produce a row whose value prints as null, but count(x) still counts it as 1. #2383

Description

@Silence6666668

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:

c
---
1

Expected behavior
The result should be:

c
---
0

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:

  1. Plain count(null) behaves correctly:
SELECT * FROM cypher('fuzz_graph', $$
  RETURN count(null) AS c
$$) AS (c agtype);

Observed result:

0
  1. 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:

0
  1. 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:

null

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions