Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue #1002 Invalid reuse of variables in MATCH clause #1003

Merged
merged 2 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion regress/expected/cypher_match.out
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ LINE 2: MATCH (r0)-[]->() MATCH ()-[r0]->() RETURN r0
SELECT * FROM cypher('cypher_match', $$
MATCH ()-[r0]->() MATCH ()-[]->(r0) RETURN r0
$$) AS (r0 agtype);
ERROR: variable 'r0' is for a edge
ERROR: variable 'r0' is for an edge
LINE 2: MATCH ()-[r0]->() MATCH ()-[]->(r0) RETURN r0
^
SELECT * FROM cypher('cypher_match', $$
Expand Down Expand Up @@ -2052,6 +2052,58 @@ SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) RETUR
ERROR: duplicate edge variable 'b' within a clause
LINE 1: ...pher('cypher_match', $$ MATCH p=(a)-[b:knows]->()-[b]->(a) R...
^
SELECT * FROM cypher('cypher_match', $$ MATCH p=(p) RETURN p $$)as (p agtype);
ERROR: variable "p" is for a path
LINE 1: SELECT * FROM cypher('cypher_match', $$ MATCH p=(p) RETURN p...
^
SELECT * FROM cypher('cypher_match', $$ MATCH p=(p)-[]->() RETURN p $$)as (p agtype);
ERROR: variable "p" is for a path
LINE 1: SELECT * FROM cypher('cypher_match', $$ MATCH p=(p)-[]->() R...
^
SELECT * FROM cypher('cypher_match', $$ MATCH p=()-[p]->() RETURN p $$)as (p agtype);
ERROR: variable "p" is for a path
LINE 1: ...ELECT * FROM cypher('cypher_match', $$ MATCH p=()-[p]->() RE...
^
SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH (p) RETURN p $$)as (p agtype);
ERROR: variable 'p' already exists
LINE 1: ... FROM cypher('cypher_match', $$ MATCH p=() MATCH (p) RETURN ...
^
SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH (p)-[]->() RETURN p $$)as (p agtype);
ERROR: variable 'p' already exists
LINE 1: ... FROM cypher('cypher_match', $$ MATCH p=() MATCH (p)-[]->() ...
^
SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH ()-[p]->() RETURN p $$)as (p agtype);
ERROR: variable 'p' already exists
LINE 1: ...ROM cypher('cypher_match', $$ MATCH p=() MATCH ()-[p]->() RE...
^
SELECT * FROM cypher('cypher_match', $$ MATCH (p) MATCH p=() RETURN p $$)as (p agtype);
ERROR: variable "p" already exists
LINE 1: ... * FROM cypher('cypher_match', $$ MATCH (p) MATCH p=() RETUR...
^
SELECT * FROM cypher('cypher_match', $$ MATCH ()-[p]->() MATCH p=() RETURN p $$)as (p agtype);
ERROR: variable "p" already exists
LINE 1: ... cypher('cypher_match', $$ MATCH ()-[p]->() MATCH p=() RETUR...
^
SELECT * FROM cypher('cypher_match', $$ MATCH ()-[p *]-()-[p]-() RETURN 0 $$)as (p agtype);
ERROR: variable 'p' is for a VLE edge
LINE 1: ... FROM cypher('cypher_match', $$ MATCH ()-[p *]-()-[p]-() RET...
^
SELECT * FROM cypher('cypher_match', $$ CREATE (p) WITH p MATCH p=() RETURN p $$)as (p agtype);
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 ...
^
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...
^
SELECT * FROM cypher('cypher_match', $$ CREATE p=() WITH p MATCH ()-[p]->() RETURN p $$)as (p agtype);
ERROR: variable 'p' already exists
LINE 1: ...er('cypher_match', $$ CREATE p=() WITH p MATCH ()-[p]->() RE...
^
--
-- Default alias check (issue #883)
--
Expand Down
53 changes: 53 additions & 0 deletions regress/expected/cypher_vle.out
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,59 @@ NOTICE: graph "mygraph" has been dropped

(1 row)

-- invalid reuse of VLE
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-()-[p *]-() RETURN p $$)as (p agtype);
ERROR: variable 'p' is for an edge
LINE 1: ...CT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-()-[p *]-() R...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p]-() RETURN p $$)as (p agtype);
ERROR: variable 'p' is for a VLE edge
LINE 1: ... * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p]-() RET...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH (p)-[p *]-() RETURN p $$)as (p agtype);
ERROR: variable 'p' is for a vertex
LINE 1: SELECT * FROM cypher('cypher_vle', $$ MATCH (p)-[p *]-() RET...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-(p) RETURN p $$)as (p agtype);
ERROR: variable 'p' is for a VLE edge
LINE 1: ...CT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-(p) RETURN ...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH p=()-[p *]-() RETURN p $$)as (p agtype);
ERROR: variable "p" is for a path
LINE 1: SELECT * FROM cypher('cypher_vle', $$ MATCH p=()-[p *]-() RE...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p *]-() RETURN p $$)as (p agtype);
ERROR: duplicate variable 'p'
LINE 1: ... * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p *]-() R...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
ERROR: variable 'p' is for an edge
LINE 1: ... cypher('cypher_vle', $$ MATCH ()-[p]-() MATCH ()-[p *]-() R...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p]-() RETURN p $$)as (p agtype);
ERROR: variable 'p' is for a VLE edge
LINE 1: ...ypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p]-() RET...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
ERROR: duplicate variable 'p'
LINE 1: ...ypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p *]-() R...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH (p) MATCH ()-[p *]-() RETURN p $$)as (p agtype);
ERROR: variable 'p' is for a vertex
LINE 1: ...* FROM cypher('cypher_vle', $$ MATCH (p) MATCH ()-[p *]-() R...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH (p) RETURN p $$)as (p agtype);
ERROR: variable 'p' is for a VLE edge
LINE 1: ... cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH (p) RETURN ...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH p=() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
ERROR: variable 'p' already exists
LINE 1: ... FROM cypher('cypher_vle', $$ MATCH p=() MATCH ()-[p *]-() R...
^
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH p=() RETURN p $$)as (p agtype);
ERROR: variable "p" already exists
LINE 1: ...M cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH p=() RETUR...
^
--
-- Clean up
--
Expand Down
15 changes: 15 additions & 0 deletions regress/sql/cypher_match.sql
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,21 @@ SELECT * FROM cypher('cypher_match', $$ MATCH p=(a)-[b]->()-[b:knows]->(a) RETUR
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: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);
SELECT * FROM cypher('cypher_match', $$ MATCH p=(p) RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH p=(p)-[]->() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH p=()-[p]->() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH (p) RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH (p)-[]->() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH p=() MATCH ()-[p]->() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH (p) MATCH p=() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH ()-[p]->() MATCH p=() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ MATCH ()-[p *]-()-[p]-() RETURN 0 $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ CREATE (p) WITH p MATCH p=() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ CREATE p=() WITH p MATCH (p) RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ CREATE ()-[p:knows]->() WITH p MATCH p=() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_match', $$ CREATE p=() WITH p MATCH ()-[p]->() RETURN p $$)as (p agtype);



--
-- Default alias check (issue #883)
Expand Down
15 changes: 15 additions & 0 deletions regress/sql/cypher_vle.sql
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,21 @@ DROP FUNCTION show_list_use_vle;

SELECT drop_graph('mygraph', true);

-- invalid reuse of VLE
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-()-[p *]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH (p)-[p *]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-(p) RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH p=()-[p *]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-()-[p *]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p]-() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH (p) MATCH ()-[p *]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH (p) RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH p=() MATCH ()-[p *]-() RETURN p $$)as (p agtype);
SELECT * FROM cypher('cypher_vle', $$ MATCH ()-[p *]-() MATCH p=() RETURN p $$)as (p agtype);

--
-- Clean up
--
Expand Down
126 changes: 117 additions & 9 deletions src/backend/parser/cypher_clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -3559,6 +3559,7 @@ static Node *create_property_constraints(cypher_parsestate *cpstate,
static List *transform_match_path(cypher_parsestate *cpstate, Query *query,
cypher_path *path)
{
ParseState *pstate = (ParseState *)cpstate;
List *qual = NIL;
List *entities = NIL;
FuncCall *duplicate_edge_qual;
Expand All @@ -3572,6 +3573,15 @@ static List *transform_match_path(cypher_parsestate *cpstate, Query *query,
{
TargetEntry *path_te;

if (findTarget(query->targetList, path->var_name) != NULL)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable \"%s\" already exists",
path->var_name),
parser_errposition(pstate, path->location)));
}

path_te = transform_match_create_path_variable(cpstate, path,
entities);
query->targetList = lappend(query->targetList, path_te);
Expand Down Expand Up @@ -3649,13 +3659,51 @@ static transform_entity *transform_VLE_edge_entity(cypher_parsestate *cpstate,

/*
* If we have a variable name (rel name), make the target entry. Otherwise,
* there isn't a reason to create one.
* there isn't a reason to create one. Additionally, verify that it is not
* reused.
*/
if (rel->name != NULL)
{
FuncExpr *fexpr;
List *args = list_make1(var);
Oid func_oid = InvalidOid;
transform_entity *entity = NULL;

te = findTarget(query->targetList, rel->name);
entity = find_variable(cpstate, rel->name);

/* If the variable already exists, error out */
if (te && entity)
{
if (entity->type == ENT_VERTEX)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' is for a vertex", rel->name),
parser_errposition(pstate, rel->location)));
}
else if (entity->type == ENT_EDGE)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' is for an edge", rel->name),
parser_errposition(pstate, rel->location)));
}
else
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("duplicate variable '%s'", rel->name),
parser_errposition(pstate, rel->location)));
}
}
else if (te && !entity)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' already exists", rel->name),
parser_errposition(pstate, rel->location)));
}

/*
* Get the oid for the materialize function that returns a list of
Expand Down Expand Up @@ -3803,11 +3851,22 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,
*/
if (node->name != NULL)
{
Node *expr;

if (path->var_name && strcmp(node->name, path->var_name) == 0)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable \"%s\" is for a path",
node->name),
parser_errposition(pstate, node->location)));
}

/*
* Checks the previous clauses to see if the variable already
* exists.
*/
Node *expr = colNameToVar(pstate, node->name, false,
expr = colNameToVar(pstate, node->name, false,
node->location);
if (expr != NULL)
{
Expand Down Expand Up @@ -3910,6 +3969,16 @@ static List *transform_match_entities(cypher_parsestate *cpstate, Query *query,

rel = lfirst(lc);

if (rel->name && path->var_name &&
strcmp(rel->name, path->var_name) == 0)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable \"%s\" is for a path",
rel->name),
parser_errposition(pstate, rel->location)));
}

/*
* There are 2 edge cases - 1) a regular edge and 2) a VLE edge.
* A VLE edge is not added like a regular edge - it is a function.
Expand Down Expand Up @@ -4326,11 +4395,29 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
}

/* If the variable already exists, verify that it is for an edge */
if (refs_var && entity->type != ENT_EDGE)
if (refs_var)
{
if (entity->type == ENT_VERTEX)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' is for a vertex", rel->name),
parser_errposition(pstate, rel->location)));
}
else if (entity->type == ENT_VLE_EDGE)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' is for a VLE edge", rel->name),
parser_errposition(pstate, rel->location)));
}
}

else if (te && !entity)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("variable '%s' is for a vertex", rel->name),
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' already exists", rel->name),
parser_errposition(pstate, rel->location)));
}
}
Expand Down Expand Up @@ -4393,7 +4480,7 @@ static Expr *transform_cypher_edge(cypher_parsestate *cpstate,
if (expr == NULL && refs_var)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("duplicate edge variable '%s' within a clause",
rel->name),
parser_errposition(pstate, rel->location)));
Expand Down Expand Up @@ -4545,11 +4632,32 @@ static Expr *transform_cypher_node(cypher_parsestate *cpstate,
}

/* If the variable already exists, verify that it is for a vertex */
if (refs_var && entity->type != ENT_VERTEX)
if (refs_var)
{
if (entity->type == ENT_EDGE)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' is for an edge", node->name),
parser_errposition(pstate, node->location)));
}
else if (entity->type == ENT_VLE_EDGE)
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' is for a VLE edge", node->name),
parser_errposition(pstate, node->location)));
}
}

/* If their is a te but no entity, it implies that their is
* some variable that exists but not an edge,vle or a vertex
*/
else if (te && !entity)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("variable '%s' is for a edge", node->name),
(errcode(ERRCODE_DUPLICATE_ALIAS),
errmsg("variable '%s' already exists", node->name),
parser_errposition(pstate, node->location)));
}
}
Expand Down