apache / apache/age

After a multi-row path `CREATE`, `WITH * CREATE (...) DELETE ...` changes cardinality instead of preserving the incoming rows.

Open
#2,565 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C
Stars
4.8k
Forks
523
Avg merge
1d 2h
Merged PRs (30d)
9

Description

**Environment**

Apache AGE `1.8.0` on PostgreSQL `18.6`, Docker image
`apache/age:release_PG18_1.8.0`.

**Setup**

Run this setup separately on two fresh graphs, one named
`age_issue_case20_left` and one named `age_issue_case20_right`:

```sql
LOAD 'age';
SET search_path = ag_catalog, public;
SELECT ag_catalog.create_graph('age_issue_case20_left');

SELECT *
FROM ag_catalog.cypher('age_issue_case20_left', $$
CREATE (a {id: 46}),
(b1 {k7: -1, k6: 2}),
(b2 {k7: -1, k6: 2}),
(c:l1 {k6: 3, klist: [4]}),
(n1:l0 {id: 10}),
(n0 {k4: [1]}),
(a)<-[:rt10 {k8: 2147483647}]-(b1),
(a)<-[:rt10 {k8: 2147483647}]-(b2),
(b1)<-[:R {k6: 3, klist: [4]}]-(c),
(b2)<-[:R {k6: 3, klist: [4]}]-(c),
(c)-[:X]->(n1)-[:Y]->(n0)
$$) AS (v agtype);
```

For the right graph, replace both graph-name occurrences above with
`age_issue_case20_right`.

**Baseline query** (run on the left graph)

```sql
SELECT *
FROM ag_catalog.cypher('age_issue_case20_left', $$
MATCH p = ({id: 46})<-[:rt10 {k8: 2147483647}]-({k7: -1, k6: 2})
<-[r1 {k6: 3, klist: [4]}]-(:l1)-[]->(n1:l0)
-[r0]->(n0 {k4: [1]})
WHERE size(n0.k4) >= 0
CREATE (n1)-[:rt5 {id: 421}]->(n0)-[:rt4 {id: 422}]->(:l1 {id: 128})
RETURN count(*) AS rows
$$) AS (rows agtype);
```

**Barrier query** (run on the right graph)

Use the same query, but insert the following clauses before the final return:

```cypher
WITH *
CREATE (__dinkel_n0)
DELETE __dinkel_n0
WITH n1, n0, r1, r0, p
```

**Expected**

Both queries preserve the two input rows and return `2`.

**Actual**

On Apache AGE `1.8.0` / PostgreSQL `18.6`, the baseline returns `2`, while
the barrier query returns `4`. The result rows include duplicated bindings;
the barrier also creates twice as many path-write entities. A read-only
`WITH *` control preserves the two rows. This is a real AGE cardinality
defect, not an outer-column-definition, JDBC, probe, or fingerprint issue.

Contributor guide

Open the contributing guide

Research direction

Start by running the setup separately on the two fresh graphs, then compare the baseline and barrier queries through ag_catalog.cypher. The defect is fixed when both queries preserve two rows, the barrier produces no duplicate bindings or extra path-write entities, and the read-only WITH * control still preserves cardinality.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, postgresql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.