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.)?
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:
Expected behavior
There are two plausible interpretations:
-
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.
-
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
Describe the bug
When a
MATCHproduces the same entity in multiple result rows, a writableexpression 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
SETclause twice, but its stored value is only incremented once. I wouldlike to confirm whether this is the intended AGE behavior or a correctness
issue.
How are you accessing AGE (Command line, driver, etc.)?
psqlWhat data setup do we need to do?
The same
Tvertex is now matched once for each outgoingRrelationship.What is the necessary configuration info needed?
What is the command that caused the error?
Observed result:
Reading the stored value:
produces:
Expected behavior
There are two plausible interpretations:
If
SETis applied to each matched row in turn and a later row observesthe write made for an earlier row, both rows should increment
cnt:The final stored value would be
2.If right-hand-side expressions are evaluated from the input state of the
SETclause without writes becoming visible between its input rows, bothrows may compute
0 + 1and the final stored value may remain1.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
cntvalue 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):
801417404978823bd8732452c3f7959017584785psqlAdditional context