Skip to content

Clarify SET semantics when one entity appears in multiple result rows #2487

Description

@crdv7

Describe the bug

When a MATCH produces the same entity in multiple result rows, a writable
expression that depends on the entity's previous property value is evaluated
from the original entity state carried by each row.

In the example below, the same vertex appears in two rows and reaches the
SET clause twice, but its stored value is only incremented once. I would
like to confirm whether this is the intended AGE behavior or a correctness
issue.

How are you accessing AGE (Command line, driver, etc.)?

  • psql

What data setup do we need to do?

LOAD 'age';
SET search_path = ag_catalog, "$user", public;

SELECT create_graph('repeated_row_set');

SELECT * FROM cypher('repeated_row_set', $$
    CREATE (n:T {cnt: 0})-[:R]->(:M {v: 1}),
           (n)-[:R]->(:M {v: 2})
$$) AS (a agtype);

The same T vertex is now matched once for each outgoing R relationship.

What is the necessary configuration info needed?

  • No additional extension or non-default AGE configuration is required.

What is the command that caused the error?

SELECT * FROM cypher('repeated_row_set', $$
    MATCH (n:T)-[:R]->(m:M)
    SET n.cnt = n.cnt + 1
    RETURN n.cnt, m.v
$$) AS (cnt agtype, mv agtype);

Observed result:

 cnt | mv
-----+----
 1   | 1
 1   | 2

Reading the stored value:

SELECT * FROM cypher('repeated_row_set', $$
    MATCH (n:T)
    RETURN n.cnt
$$) AS (cnt agtype);

produces:

 cnt
-----
 1

Expected behavior

There are two plausible interpretations:

  1. If SET is applied to each matched row in turn and a later row observes
    the write made for an earlier row, both rows should increment cnt:

    row 1: 0 + 1 -> 1
    row 2: 1 + 1 -> 2
    

    The final stored value would be 2.

  2. If right-hand-side expressions are evaluated from the input state of the
    SET clause without writes becoming visible between its input rows, both
    rows may compute 0 + 1 and the final stored value may remain 1.

Could the community confirm which interpretation is intended? Whichever
behavior is chosen should be deterministic and documented. If AGE intends
the first interpretation, the observed result is a correctness bug.

The
Cypher clause-composition documentation
states that most clauses are applied to each row of the intermediate result
table in turn, but it does not explicitly resolve multiple input rows that
refer to the same entity within one writable clause. For comparison, the
equivalent query in Neo4j accumulates both updates and stores a final cnt
value of 2.

PostgreSQL takes a different approach for
UPDATE ... FROM:
when multiple join rows identify the same target row, only one join row is
used for the update. AGE is implemented on PostgreSQL but exposes Cypher, so
neither precedent alone establishes the intended AGE semantics.

Environment (please complete the following information):

  • AGE baseline commit: 801417404978823bd8732452c3f7959017584785
  • PostgreSQL: 18.4
  • Access method: psql
  • OS: Linux x86-64

Additional context

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