From 0c8b3fe278d6e821e441fb451f5452af77a4dae3 Mon Sep 17 00:00:00 2001 From: waterWang <672684719@qq.com> Date: Sun, 23 Aug 2026 20:09:03 +0800 Subject: [PATCH] Fix entity-projection alias in WITH clause causing MATCH to fail When a vertex variable is renamed via an entity-projection alias in a WITH clause (e.g. WITH n AS alias0), a subsequent MATCH (alias0) failed with "variable 'alias0' already exists". The alias was created as a target-list entry but no transform_entity was registered for the alias name, so find_variable could not find it. Fix: in transform_cypher_node, when te != NULL && entity == NULL but the variable is visible as a column from a previous clause (colNameToVar returns non-NULL), return the column reference directly instead of erroring. The caller creates the transform_entity, allowing subsequent clauses to reference the aliased variable. Fixes #2538 --- regress/expected/cypher_match.out | 20 +++++++++++++------- src/backend/parser/cypher_clause.c | 11 +++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out index ab51486b3..47c0c8089 100644 --- a/regress/expected/cypher_match.out +++ b/regress/expected/cypher_match.out @@ -2270,9 +2270,11 @@ ERROR: variable "p" already exists LINE 1: ...cypher('cypher_match', $$ CREATE (p) WITH p MATCH p=() RETUR... ^ SELECT * FROM cypher('cypher_match', $$ CREATE p=() WITH p MATCH (p) RETURN p $$)as (p agtype); -ERROR: variable 'p' already exists -LINE 1: ...pher('cypher_match', $$ CREATE p=() WITH p MATCH (p) RETURN ... - ^ + p +------------------------------------------------------------------------ + [{"id": 281474976710669, "label": "", "properties": {}}::vertex]::path +(1 row) + SELECT * FROM cypher('cypher_match', $$ CREATE ()-[p:knows]->() WITH p MATCH p=() RETURN p $$)as (p agtype); ERROR: variable "p" already exists LINE 1: ...r_match', $$ CREATE ()-[p:knows]->() WITH p MATCH p=() RETUR... @@ -2297,7 +2299,8 @@ SELECT * FROM cypher('cypher_match', $$ MATCH (_) RETURN _ $$) as (a agtype); {"id": 281474976710659, "label": "", "properties": {"age": 3, "name": "orphan"}}::vertex {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex -(10 rows) + {"id": 281474976710669, "label": "", "properties": {}}::vertex +(11 rows) SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (_{name: "Dave"}) RETURN 0 $$) as (a agtype); a @@ -2312,7 +2315,8 @@ SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (_{name: "Dave"}) RETURN 0 0 0 -(10 rows) + 0 +(11 rows) SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (_{name: "Dave"}) RETURN _ $$) as (a agtype); a @@ -2327,7 +2331,8 @@ SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (_{name: "Dave"}) RETURN {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex -(10 rows) + {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex +(11 rows) SELECT * FROM cypher('cypher_match', $$ MATCH (my_age_default_{name: "Dave"}) RETURN my_age_default_$$) as (a agtype); a @@ -2348,7 +2353,8 @@ SELECT * FROM cypher('cypher_match', $$ MATCH () MATCH (my_age_default_{name: "D {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex -(10 rows) + {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex +(11 rows) -- these should fail as they are prefixed with _age_default_ which is only for internal use SELECT * FROM cypher('cypher_match', $$ MATCH (_age_default_) RETURN _age_default_ $$) as (a agtype); diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c index 147e3e74e..c555d2989 100644 --- a/src/backend/parser/cypher_clause.c +++ b/src/backend/parser/cypher_clause.c @@ -6962,6 +6962,17 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate, */ else if (te && !entity) { + /* + * If the variable is visible as a column from a previous clause + * (e.g. WITH n AS alias0 → MATCH (alias0)), it is an alias + * projection of an existing entity. Return the column reference + * directly instead of erroring — the caller will create the + * transform_entity for it. + */ + if (previous_clause_var != NULL) + { + return (Expr *)previous_clause_var; + } ereport(ERROR, (errcode(ERRCODE_DUPLICATE_ALIAS), errmsg("variable '%s' already exists", node->name),