Skip to content

Commit bcc3e74

Browse files
gregfeliceclaude
andcommitted
Address review feedback: fix variable registration for deferred label check
When the deferred label validity check (DML predecessor + non-existent label) found an invalid label, the code skipped transform_match_pattern() entirely, which meant MATCH-introduced variables were never registered in the namespace. This would cause errors if a later clause referenced those variables (e.g., RETURN p). Fix: mirror the early-check strategy by injecting a paradoxical WHERE (true = false) and always calling transform_match_pattern(). Variables get registered normally; zero rows are returned via the impossible qual. Also add ORDER BY to multi-row regression tests for deterministic output, and add a test case for DML predecessor + non-existent label + returning a MATCH-introduced variable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d2d7245 commit bcc3e74

File tree

3 files changed

+37
-18
lines changed

3 files changed

+37
-18
lines changed

regress/expected/cypher_match.out

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3652,7 +3652,7 @@ SELECT * FROM cypher('issue_2193', $$
36523652
(b:Person {name: 'Tom', livesIn: 'Copenhagen'})
36533653
WITH a, b
36543654
MATCH (p:Person)
3655-
RETURN p.name
3655+
RETURN p.name ORDER BY p.name
36563656
$$) AS (result agtype);
36573657
result
36583658
--------
@@ -3677,7 +3677,7 @@ SELECT * FROM cypher('issue_2193', $$
36773677
CREATE (a:City {name: 'Paris'})
36783678
WITH a
36793679
MATCH (c:City)
3680-
RETURN c.name
3680+
RETURN c.name ORDER BY c.name
36813681
$$) AS (result agtype);
36823682
result
36833683
----------
@@ -3694,6 +3694,18 @@ $$) AS (result agtype);
36943694
--------
36953695
(0 rows)
36963696

3697+
-- MATCH on non-existent label after DML predecessor still returns 0 rows
3698+
-- and MATCH-introduced variable (p) is properly registered
3699+
SELECT * FROM cypher('issue_2193', $$
3700+
CREATE (a:Person {name: 'Alice'})
3701+
WITH a
3702+
MATCH (p:NonExistentLabel)
3703+
RETURN p
3704+
$$) AS (result agtype);
3705+
result
3706+
--------
3707+
(0 rows)
3708+
36973709
SELECT drop_graph('issue_2193', true);
36983710
NOTICE: drop cascades to 4 other objects
36993711
DETAIL: drop cascades to table issue_2193._ag_label_vertex

regress/sql/cypher_match.sql

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,7 +1506,7 @@ SELECT * FROM cypher('issue_2193', $$
15061506
(b:Person {name: 'Tom', livesIn: 'Copenhagen'})
15071507
WITH a, b
15081508
MATCH (p:Person)
1509-
RETURN p.name
1509+
RETURN p.name ORDER BY p.name
15101510
$$) AS (result agtype);
15111511

15121512
-- Single CREATE + MATCH on brand-new label
@@ -1522,7 +1522,7 @@ SELECT * FROM cypher('issue_2193', $$
15221522
CREATE (a:City {name: 'Paris'})
15231523
WITH a
15241524
MATCH (c:City)
1525-
RETURN c.name
1525+
RETURN c.name ORDER BY c.name
15261526
$$) AS (result agtype);
15271527

15281528
-- MATCH on non-existent label without DML predecessor still returns 0 rows
@@ -1531,6 +1531,15 @@ SELECT * FROM cypher('issue_2193', $$
15311531
RETURN x
15321532
$$) AS (result agtype);
15331533

1534+
-- MATCH on non-existent label after DML predecessor still returns 0 rows
1535+
-- and MATCH-introduced variable (p) is properly registered
1536+
SELECT * FROM cypher('issue_2193', $$
1537+
CREATE (a:Person {name: 'Alice'})
1538+
WITH a
1539+
MATCH (p:NonExistentLabel)
1540+
RETURN p
1541+
$$) AS (result agtype);
1542+
15341543
SELECT drop_graph('issue_2193', true);
15351544

15361545
--

src/backend/parser/cypher_clause.c

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2918,8 +2918,6 @@ static Query *transform_cypher_match_pattern(cypher_parsestate *cpstate,
29182918
}
29192919
else
29202920
{
2921-
bool valid_labels = true;
2922-
29232921
if (clause->prev)
29242922
{
29252923
RangeTblEntry *rte;
@@ -2971,21 +2969,21 @@ static Query *transform_cypher_match_pattern(cypher_parsestate *cpstate,
29712969
if (clause_chain_has_dml(clause->prev) &&
29722970
!match_check_valid_label(self, cpstate))
29732971
{
2974-
valid_labels = false;
2972+
cypher_bool_const *l = make_ag_node(cypher_bool_const);
2973+
cypher_bool_const *r = make_ag_node(cypher_bool_const);
2974+
2975+
l->boolean = true;
2976+
l->location = -1;
2977+
r->boolean = false;
2978+
r->location = -1;
2979+
2980+
where = (Node *)makeSimpleA_Expr(AEXPR_OP, "=",
2981+
(Node *)l,
2982+
(Node *)r, -1);
29752983
}
29762984
}
29772985

2978-
if (valid_labels)
2979-
{
2980-
transform_match_pattern(cpstate, query, self->pattern, where);
2981-
}
2982-
else
2983-
{
2984-
query->rtable = pstate->p_rtable;
2985-
query->rteperminfos = pstate->p_rteperminfos;
2986-
query->jointree = makeFromExpr(pstate->p_joinlist,
2987-
makeBoolConst(false, false));
2988-
}
2986+
transform_match_pattern(cpstate, query, self->pattern, where);
29892987
}
29902988

29912989
markTargetListOrigins(pstate, query->targetList);

0 commit comments

Comments
 (0)