gregfelice opened a new pull request, #2444:
URL: https://github.com/apache/age/pull/2444
## Problem
A single-node labeled pattern used as a boolean expression — e.g. `WHERE
(a:Person)` or `WHERE EXISTS((a:Person))` — is accepted but does **not** test
the bound vertex's label. It evaluates as trivially true, so the predicate
matches every row.
Repro (on `master`):
```sql
SELECT * FROM cypher('g', $$ CREATE (:Person {name:'Alice'}), (:Animal
{name:'Rex'}) $$) AS (r agtype);
-- returns BOTH Alice and Rex; should return only Alice
SELECT * FROM cypher('g', $$ MATCH (a) WHERE (a:Person) RETURN a.name $$) AS
(n agtype);
```
## Root cause
A single-node pattern expression desugars to an `EXISTS` sub-pattern.
`make_path_join_quals()` returns early for vertex-only patterns
(`list_length(entities) < 3`), emitting no quals. With no edge to carry a
correlation, the sub-pattern references nothing from the enclosing query, so
the planner produces an **uncorrelated one-time InitPlan** that is true
whenever any vertex of that label exists — independent of the outer row.
Relationship patterns are unaffected: the edge-driven join (`start_id =
a.id`) correlates them to the outer variable, which is why `WHERE
(a)-[:R]->(b)` works correctly.
`EXPLAIN` before the fix — uncorrelated `InitPlan` with a `One-Time Filter`:
```
InitPlan 1
-> Result
One-Time Filter: ((InitPlan 1).col1)::agtype
```
After the fix — correlated `SubPlan` referencing the outer `a_1`:
```
SubPlan 1
-> Result
One-Time Filter: ((_extract_label_id(a_1.id))::integer = 3)
```
## Fix
In `make_path_join_quals()`, for a vertex-only pattern whose vertex carries
a non-default label **and** whose variable is declared in an ancestor parse
state (a correlated reference), emit an explicit label-id filter. `make_qual()`
builds a name-based id reference that resolves to the outer variable, so the
filter both **correlates** the sub-pattern to that variable and **enforces**
the label.
Freshly scanned, non-correlated vertices (no ancestor binding) are
untouched, so `MATCH (a:Person)` and "does any X exist" `EXISTS` checks behave
exactly as before.
## Testing
Added regression coverage to `pattern_expression` against a graph containing
a non-`Person` vertex:
- `WHERE (a:Person)` → only the `:Person` vertices
- `WHERE NOT (a:Person)` → only the non-`Person` vertex
- `WHERE EXISTS((a:Company))` → only the `:Company` vertex
All 41 regression tests pass (`make installcheck`).
## Note
`RETURN (a:Label)` in a **projection** on an unlabeled bound variable still
errors with "multiple labels for variable" — that is a separate, pre-existing
guard, orthogonal to this fix, and is intentionally left unchanged.
Fixes #2443
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]