From 0c4a98518460e9cb66feab0f10bc58c636dd4d6e Mon Sep 17 00:00:00 2001 From: John Gemignani Date: Fri, 17 Mar 2023 09:36:05 -0700 Subject: [PATCH] Fix property constraints against resolved variables (#724) Fixed an issue where an already resolved variable, when used for a property constraint, errored out. See #724 for more details. This is the second part of the fix for the match property constraint bug regarding variable reuse. The prior fix addressed clause-to-clause, this fix addresses within a clause. Adjusted and added additional regression tests. Co-authored-by: Dehowe Feng --- regress/expected/cypher_match.out | 118 ++++++++++++++++++++++++++--- regress/sql/cypher_match.sql | 40 ++++++++-- src/backend/parser/cypher_clause.c | 68 +++++++++++------ 3 files changed, 186 insertions(+), 40 deletions(-) diff --git a/regress/expected/cypher_match.out b/regress/expected/cypher_match.out index f3817a50d..0e5964067 100644 --- a/regress/expected/cypher_match.out +++ b/regress/expected/cypher_match.out @@ -502,25 +502,45 @@ SELECT * FROM cypher('cypher_match', $$MATCH (n:v)$$) AS (a agtype); ERROR: syntax error at end of input LINE 1: SELECT * FROM cypher('cypher_match', $$MATCH (n:v)$$) AS (a ... ^ ---Invalid Variables +--invalid variable reuse, these should fail SELECT * FROM cypher('cypher_match', $$ MATCH (a)-[]-()-[]-(a:v1) RETURN a $$) AS (a agtype); -ERROR: variable a already exists +ERROR: multiple labels for variable 'a' are not supported LINE 2: MATCH (a)-[]-()-[]-(a:v1) RETURN a ^ +SELECT * FROM cypher('cypher_match', $$ + MATCH (a)-[]-(a:v2)-[]-(a) RETURN a +$$) AS (a agtype); +ERROR: multiple labels for variable 'a' are not supported +LINE 2: MATCH (a)-[]-(a:v2)-[]-(a) RETURN a + ^ +SELECT * FROM cypher('cypher_match', $$ + MATCH (a)-[]-(a:v1) RETURN a +$$) AS (a agtype); +ERROR: multiple labels for variable 'a' are not supported +LINE 2: MATCH (a)-[]-(a:v1) RETURN a + ^ +SELECT * FROM cypher('cypher_match', $$ + MATCH (a)-[]-(a)-[]-(a:v1) RETURN a +$$) AS (a agtype); +ERROR: multiple labels for variable 'a' are not supported +LINE 2: MATCH (a)-[]-(a)-[]-(a:v1) RETURN a + ^ +--Valid variable reuse, although why would you want to do it this way? SELECT * FROM cypher('cypher_match', $$ MATCH (a:v1)-[]-()-[a]-() RETURN a $$) AS (a agtype); -ERROR: variable a already exists +ERROR: variable 'a' is for a vertex LINE 2: MATCH (a:v1)-[]-()-[a]-() RETURN a ^ SELECT * FROM cypher('cypher_match', $$ - MATCH (a:v1)-[]-()-[]-(a {id:'will_fail'}) RETURN a + MATCH (a:v1)-[]-()-[]-(a {id:'will_not_fail'}) RETURN a $$) AS (a agtype); -ERROR: variable a already exists -LINE 2: MATCH (a:v1)-[]-()-[]-(a {id:'will_fail'}) RETURN a - ^ + a +--- +(0 rows) + --Incorrect Labels SELECT * FROM cypher('cypher_match', $$MATCH (n)-[:v]-() RETURN n$$) AS (n agtype); n @@ -1509,14 +1529,14 @@ SELECT * FROM cypher('cypher_match', $$ (1 row) SELECT * FROM cypher('cypher_match', $$ - MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE + MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE (a)-[:knows {relationship: "friends", years: 3}]->(b) $$) as (r agtype); r --- (0 rows) SELECT * FROM cypher('cypher_match', $$ - MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE + MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE (a)-[:knows {relationship: "enemies", years: 4}]->(b) $$) as (r agtype); r --- @@ -1534,7 +1554,7 @@ SELECT * FROM cypher('cypher_match', $$ {"id": 1407374883553283, "label": "e1", "end_id": 281474976710661, "start_id": 281474976710660, "properties": {}}::edge (6 rows) --- check reuse of 'a' +-- check reuse of 'a' clause-to-clause - vertices SELECT * FROM cypher('cypher_match', $$ MATCH (a {age:4}) RETURN a $$) as (a agtype); a @@ -1614,7 +1634,7 @@ SELECT * FROM cypher('cypher_match', $$ {"id": 281474976710666, "label": "", "properties": {"age": 6}}::vertex (2 rows) --- check reuse of 'r' +-- check reuse of 'r' clause-to-clause - edges SELECT * FROM cypher('cypher_match', $$ MATCH ()-[r]-() RETURN r $$) as (r agtype); r @@ -1672,6 +1692,82 @@ SELECT * FROM cypher('cypher_match', $$ --- (0 rows) +-- check reuse within clause - vertices +SELECT * FROM cypher('cypher_match', $$ CREATE (u {name: "Dave"})-[:knows]->({name: "John"})-[:knows]->(u) RETURN u $$) as (u agtype); + u +------------------------------------------------------------------------------ + {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex +(1 row) + +SELECT * FROM cypher('cypher_match', $$ MATCH p=(u)-[]-()-[]-(u) RETURN p $$)as (p agtype); + p +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path + [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path + [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path + [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path +(4 rows) + +SELECT * FROM cypher('cypher_match', $$ MATCH p=(u)-[]->()-[]->(u) RETURN p $$)as (p agtype); + p +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path + [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path +(2 rows) + +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[]->()-[]->(a {name: "Dave"}) RETURN p $$)as (p agtype); + p +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path +(1 row) + +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[]->()-[]->(a {name: "John"}) RETURN p $$)as (p agtype); + p +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path +(1 row) + +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a {name: "Dave"}) RETURN p $$)as (p agtype); + p +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path +(1 row) + +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a {name: "John"}) RETURN p $$)as (p agtype); + p +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path +(1 row) + +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a) RETURN p $$)as (p agtype); + p +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex]::path +(1 row) + +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a) RETURN p $$)as (p agtype); + p +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex, {"id": 4785074604081155, "label": "knows", "end_id": 281474976710667, "start_id": 281474976710668, "properties": {}}::edge, {"id": 281474976710667, "label": "", "properties": {"name": "Dave"}}::vertex, {"id": 4785074604081156, "label": "knows", "end_id": 281474976710668, "start_id": 281474976710667, "properties": {}}::edge, {"id": 281474976710668, "label": "", "properties": {"name": "John"}}::vertex]::path +(1 row) + +-- these are illegal and should fail +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) RETURN p $$)as (p agtype); +ERROR: duplicate edge variable 'b' within a clause +LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) R... + ^ +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]->(a) RETURN p $$)as (p agtype); +ERROR: duplicate edge variable 'b' within a clause +LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]-... + ^ +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]->(a) RETURN p $$)as (p agtype); +ERROR: duplicate edge variable 'b' within a clause +LINE 1: ...pher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]-... + ^ +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) RETURN p $$)as (p agtype); +ERROR: duplicate edge variable 'b' within a clause +LINE 1: ...pher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) R... + ^ -- -- Clean up -- diff --git a/regress/sql/cypher_match.sql b/regress/sql/cypher_match.sql index f08f8dfb0..b05e79c52 100644 --- a/regress/sql/cypher_match.sql +++ b/regress/sql/cypher_match.sql @@ -271,17 +271,27 @@ EXECUTE property_ps(agtype_build_map('props', -- need a following RETURN clause (should fail) SELECT * FROM cypher('cypher_match', $$MATCH (n:v)$$) AS (a agtype); ---Invalid Variables +--invalid variable reuse, these should fail SELECT * FROM cypher('cypher_match', $$ MATCH (a)-[]-()-[]-(a:v1) RETURN a $$) AS (a agtype); +SELECT * FROM cypher('cypher_match', $$ + MATCH (a)-[]-(a:v2)-[]-(a) RETURN a +$$) AS (a agtype); +SELECT * FROM cypher('cypher_match', $$ + MATCH (a)-[]-(a:v1) RETURN a +$$) AS (a agtype); +SELECT * FROM cypher('cypher_match', $$ + MATCH (a)-[]-(a)-[]-(a:v1) RETURN a +$$) AS (a agtype); +--Valid variable reuse, although why would you want to do it this way? SELECT * FROM cypher('cypher_match', $$ MATCH (a:v1)-[]-()-[a]-() RETURN a $$) AS (a agtype); SELECT * FROM cypher('cypher_match', $$ - MATCH (a:v1)-[]-()-[]-(a {id:'will_fail'}) RETURN a + MATCH (a:v1)-[]-()-[]-(a {id:'will_not_fail'}) RETURN a $$) AS (a agtype); --Incorrect Labels @@ -750,20 +760,19 @@ SELECT * FROM cypher('cypher_match', $$ SELECT * FROM cypher('cypher_match', $$ MATCH (a) WHERE exists(a.name) SET a.age = 4 RETURN a $$) as (a agtype); - SELECT * FROM cypher('cypher_match', $$ MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 RETURN a,b $$) as (a agtype, b agtype); SELECT * FROM cypher('cypher_match', $$ - MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE + MATCH (a),(b) WHERE a.age = 4 AND a.name = "T" AND b.age = 6 CREATE (a)-[:knows {relationship: "friends", years: 3}]->(b) $$) as (r agtype); SELECT * FROM cypher('cypher_match', $$ - MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE + MATCH (a),(b) WHERE a.age = 4 AND a.name = "orphan" AND b.age = 6 CREATE (a)-[:knows {relationship: "enemies", years: 4}]->(b) $$) as (r agtype); SELECT * FROM cypher('cypher_match', $$ MATCH (a)-[r]-(b) RETURN r $$) as (r agtype); --- check reuse of 'a' +-- check reuse of 'a' clause-to-clause - vertices SELECT * FROM cypher('cypher_match', $$ MATCH (a {age:4}) RETURN a $$) as (a agtype); SELECT * FROM cypher('cypher_match', $$ @@ -790,7 +799,7 @@ SELECT * FROM cypher('cypher_match', $$ SELECT * FROM cypher('cypher_match', $$ MATCH (a) WHERE exists(a.age) AND NOT exists(a.name) RETURN a $$) as (a agtype); --- check reuse of 'r' +-- check reuse of 'r' clause-to-clause - edges SELECT * FROM cypher('cypher_match', $$ MATCH ()-[r]-() RETURN r $$) as (r agtype); SELECT * FROM cypher('cypher_match', $$ @@ -807,6 +816,23 @@ SELECT * FROM cypher('cypher_match', $$ SELECT * FROM cypher('cypher_match', $$ MATCH ()-[r {relationship:"enemies"}]-() MATCH ()-[r {relationship:"friends"}]-() RETURN r $$) as (r agtype); +-- check reuse within clause - vertices +SELECT * FROM cypher('cypher_match', $$ CREATE (u {name: "Dave"})-[:knows]->({name: "John"})-[:knows]->(u) RETURN u $$) as (u agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(u)-[]-()-[]-(u) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(u)-[]->()-[]->(u) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[]->()-[]->(a {name: "Dave"}) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[]->()-[]->(a {name: "John"}) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a {name: "Dave"}) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a {name: "John"}) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "Dave"})-[]->()-[]->(a) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a {name: "John"})-[]->()-[]->(a) RETURN p $$)as (p agtype); + +-- these are illegal and should fail +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b]->(a) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]->(a) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b:knows]->(a) RETURN p $$)as (p agtype); +SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) RETURN p $$)as (p agtype); + -- -- Clean up -- diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c index 01b336057..ad876d8c3 100644 --- a/src/backend/parser/cypher_clause.c +++ b/src/backend/parser/cypher_clause.c @@ -4352,20 +4352,29 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate, transform_entity *entity = find_variable(cpstate, rel->name); /* - * TODO: openCypher allows a variable to be used before it - * is properly declared. This logic is not satifactory - * for that and must be better developed. + * If the variable already exists, verify that it is for an edge. + * You cannot have the same edge repeated in a path. + * You cannot have an variable that is for a vertex. */ - if (entity != NULL && - (entity->type != ENT_EDGE || - !IS_DEFAULT_LABEL_EDGE(rel->label) || - rel->props)) + if (entity != NULL) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("variable %s already exists", rel->name), - parser_errposition(pstate, rel->location))); + if (entity->type == ENT_EDGE) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("duplicate edge variable '%s' within a clause", + rel->name), + parser_errposition(pstate, rel->location))); + } + if (entity->type == ENT_VERTEX) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("variable '%s' is for a vertex", rel->name), + parser_errposition(pstate, rel->location))); + } } + return te->expr; } } @@ -4444,7 +4453,7 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate, * segmentation faults, and other errors. * * Update: Nonexistent and mismatched labels now return a NULL value to - * prevent segmentation faults, and other errors. We can also consider + * prevent segmentation faults, and other errors. We can also consider * if an all-purpose label would be useful. */ node->label = NULL; @@ -4501,22 +4510,37 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate, { transform_entity *entity = find_variable(cpstate, node->name); - /* - * TODO: openCypher allows a variable to be used before it - * is properly declared. This logic is not satifactory - * for that and must be better developed. - */ - if (entity != NULL && - (entity->type != ENT_VERTEX || - !IS_DEFAULT_LABEL_VERTEX(node->label) || - node->props)) + /* If the variable already exists, verify that it is for a vertex */ + if (entity != NULL && (entity->type != ENT_VERTEX)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("variable %s already exists", node->name), + errmsg("variable '%s' is for a edge", node->name), parser_errposition(pstate, node->location))); } + /* + * If the variable already exists, verify that any label specified + * is of the same name or scope. Reject those that aren't. + */ + if (entity != NULL) + { + cypher_node *cnode = (cypher_node *)entity->entity.node; + + if (cnode != NULL && + node != NULL && + /* allow node using a default label against resolved var */ + pg_strcasecmp(node->label, AG_DEFAULT_LABEL_VERTEX) != 0 && + /* allow labels with the same name */ + pg_strcasecmp(cnode->label, node->label) != 0) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("multiple labels for variable '%s' are not supported", node->name), + parser_errposition(pstate, node->location))); + } + } + return te->expr; } }