Skip to content

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

@Silence6666668

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions