Skip to content

Commit 80154a5

Browse files
jrgemignaniZainab-Saad
authored andcommitted
Fix issue apache#1399 EXISTS doesn't handle non-existent labels (apache#1400)
Fixed issue apache#1399 where EXISTS doesn't handle non-existent labels. However, the issue actually is due to the extra processing (read: extra distance between the AST nodes) that prevented the transform logic from being able to resolve variables created in previous clauses further than one parent up. Added logic to allow the `find_variable` function to search up the parent parse nodes. Added regression tests to cover these cases.
1 parent 20aaa01 commit 80154a5

File tree

3 files changed

+288
-23
lines changed

3 files changed

+288
-23
lines changed

regress/expected/cypher_match.out

+172
Original file line numberDiff line numberDiff line change
@@ -2829,6 +2829,167 @@ SELECT * FROM cypher('cypher_match', $$ MATCH p1=(n {name:'Dave'})-[]->() MATCH
28292829
true
28302830
(1 row)
28312831

2832+
--
2833+
-- Issue 1399 EXISTS leads to an error if a relation label does not exists as database table
2834+
--
2835+
SELECT create_graph('issue_1399');
2836+
NOTICE: graph "issue_1399" has been created
2837+
create_graph
2838+
--------------
2839+
2840+
(1 row)
2841+
2842+
-- this is an empty graph so these should return 0
2843+
SELECT * FROM cypher('issue_1399', $$
2844+
MATCH (foo)
2845+
WHERE exists((foo)-[]->())
2846+
RETURN foo
2847+
$$) as (c agtype);
2848+
c
2849+
---
2850+
(0 rows)
2851+
2852+
SELECT * FROM cypher('issue_1399', $$
2853+
MATCH (foo)
2854+
WHERE NOT exists((foo)-[]->())
2855+
RETURN foo
2856+
$$) as (c agtype);
2857+
c
2858+
---
2859+
(0 rows)
2860+
2861+
SELECT * FROM cypher('issue_1399', $$
2862+
MATCH (foo)
2863+
WHERE exists((foo)-[:BAR]->())
2864+
RETURN foo
2865+
$$) as (c agtype);
2866+
c
2867+
---
2868+
(0 rows)
2869+
2870+
SELECT * FROM cypher('issue_1399', $$
2871+
MATCH (foo)
2872+
WHERE NOT exists((foo)-[:BAR]->())
2873+
RETURN foo
2874+
$$) as (c agtype);
2875+
c
2876+
---
2877+
(0 rows)
2878+
2879+
-- this is an empty graph so these should return false
2880+
SELECT * FROM cypher('issue_1399', $$
2881+
MATCH (foo)
2882+
WHERE exists((foo)-[]->())
2883+
RETURN count(foo) > 0
2884+
$$) as (c agtype);
2885+
c
2886+
-------
2887+
false
2888+
(1 row)
2889+
2890+
SELECT * FROM cypher('issue_1399', $$
2891+
MATCH (foo)
2892+
WHERE NOT exists((foo)-[]->())
2893+
RETURN count(foo) > 0
2894+
$$) as (c agtype);
2895+
c
2896+
-------
2897+
false
2898+
(1 row)
2899+
2900+
SELECT * FROM cypher('issue_1399', $$
2901+
MATCH (foo)
2902+
WHERE exists((foo)-[:BAR]->())
2903+
RETURN count(foo) > 0
2904+
$$) as (c agtype);
2905+
c
2906+
-------
2907+
false
2908+
(1 row)
2909+
2910+
SELECT * FROM cypher('issue_1399', $$
2911+
MATCH (foo)
2912+
WHERE NOT exists((foo)-[:BAR]->())
2913+
RETURN count(foo) > 0
2914+
$$) as (c agtype);
2915+
c
2916+
-------
2917+
false
2918+
(1 row)
2919+
2920+
-- create 1 path
2921+
SELECT * FROM cypher('issue_1399', $$
2922+
CREATE (foo)-[:BAR]->() RETURN foo
2923+
$$) as (c agtype);
2924+
c
2925+
----------------------------------------------------------------
2926+
{"id": 281474976710657, "label": "", "properties": {}}::vertex
2927+
(1 row)
2928+
2929+
-- these should each return 1 row as it is a directed edge and
2930+
-- only one vertex can match.
2931+
SELECT * FROM cypher('issue_1399', $$
2932+
MATCH (foo)
2933+
WHERE exists((foo)-[]->())
2934+
RETURN foo
2935+
$$) as (c agtype);
2936+
c
2937+
----------------------------------------------------------------
2938+
{"id": 281474976710657, "label": "", "properties": {}}::vertex
2939+
(1 row)
2940+
2941+
SELECT * FROM cypher('issue_1399', $$
2942+
MATCH (foo)
2943+
WHERE NOT exists((foo)-[]->())
2944+
RETURN foo
2945+
$$) as (c agtype);
2946+
c
2947+
----------------------------------------------------------------
2948+
{"id": 281474976710658, "label": "", "properties": {}}::vertex
2949+
(1 row)
2950+
2951+
SELECT * FROM cypher('issue_1399', $$
2952+
MATCH (foo)
2953+
WHERE exists((foo)-[:BAR]->())
2954+
RETURN foo
2955+
$$) as (c agtype);
2956+
c
2957+
----------------------------------------------------------------
2958+
{"id": 281474976710657, "label": "", "properties": {}}::vertex
2959+
(1 row)
2960+
2961+
SELECT * FROM cypher('issue_1399', $$
2962+
MATCH (foo)
2963+
WHERE NOT exists((foo)-[:BAR]->())
2964+
RETURN foo
2965+
$$) as (c agtype);
2966+
c
2967+
----------------------------------------------------------------
2968+
{"id": 281474976710658, "label": "", "properties": {}}::vertex
2969+
(1 row)
2970+
2971+
-- this should return 0 rows as it can't exist - that path isn't in BAR2
2972+
SELECT * FROM cypher('issue_1399', $$
2973+
MATCH (foo)
2974+
WHERE exists((foo)-[:BAR2]->())
2975+
RETURN foo
2976+
$$) as (c agtype);
2977+
c
2978+
---
2979+
(0 rows)
2980+
2981+
-- this should return 2 rows as they all exist
2982+
SELECT * FROM cypher('issue_1399', $$
2983+
MATCH (foo)
2984+
WHERE NOT exists((foo)-[:BAR2]->())
2985+
RETURN foo
2986+
$$) as (c agtype);
2987+
c
2988+
----------------------------------------------------------------
2989+
{"id": 281474976710657, "label": "", "properties": {}}::vertex
2990+
{"id": 281474976710658, "label": "", "properties": {}}::vertex
2991+
(2 rows)
2992+
28322993
--
28332994
-- Clean up
28342995
--
@@ -2892,6 +3053,17 @@ NOTICE: graph "issue_945" has been dropped
28923053

28933054
(1 row)
28943055

3056+
SELECT drop_graph('issue_1399', true);
3057+
NOTICE: drop cascades to 3 other objects
3058+
DETAIL: drop cascades to table issue_1399._ag_label_vertex
3059+
drop cascades to table issue_1399._ag_label_edge
3060+
drop cascades to table issue_1399."BAR"
3061+
NOTICE: graph "issue_1399" has been dropped
3062+
drop_graph
3063+
------------
3064+
3065+
(1 row)
3066+
28953067
--
28963068
-- End
28973069
--

regress/sql/cypher_match.sql

+86
Original file line numberDiff line numberDiff line change
@@ -1178,13 +1178,99 @@ SELECT * FROM cypher('cypher_match', $$ MATCH p=()-[*]->() WHERE size(nodes(p))
11781178
SELECT * FROM cypher('cypher_match', $$ MATCH (n {name:'Dave'}) MATCH p=()-[*]->() WHERE nodes(p)[0] = n RETURN length(p) $$) as (length agtype);
11791179
SELECT * FROM cypher('cypher_match', $$ MATCH p1=(n {name:'Dave'})-[]->() MATCH p2=()-[*]->() WHERE p2=p1 RETURN p2=p1 $$) as (path agtype);
11801180

1181+
--
1182+
-- Issue 1399 EXISTS leads to an error if a relation label does not exists as database table
1183+
--
1184+
SELECT create_graph('issue_1399');
1185+
-- this is an empty graph so these should return 0
1186+
SELECT * FROM cypher('issue_1399', $$
1187+
MATCH (foo)
1188+
WHERE exists((foo)-[]->())
1189+
RETURN foo
1190+
$$) as (c agtype);
1191+
SELECT * FROM cypher('issue_1399', $$
1192+
MATCH (foo)
1193+
WHERE NOT exists((foo)-[]->())
1194+
RETURN foo
1195+
$$) as (c agtype);
1196+
SELECT * FROM cypher('issue_1399', $$
1197+
MATCH (foo)
1198+
WHERE exists((foo)-[:BAR]->())
1199+
RETURN foo
1200+
$$) as (c agtype);
1201+
SELECT * FROM cypher('issue_1399', $$
1202+
MATCH (foo)
1203+
WHERE NOT exists((foo)-[:BAR]->())
1204+
RETURN foo
1205+
$$) as (c agtype);
1206+
-- this is an empty graph so these should return false
1207+
SELECT * FROM cypher('issue_1399', $$
1208+
MATCH (foo)
1209+
WHERE exists((foo)-[]->())
1210+
RETURN count(foo) > 0
1211+
$$) as (c agtype);
1212+
SELECT * FROM cypher('issue_1399', $$
1213+
MATCH (foo)
1214+
WHERE NOT exists((foo)-[]->())
1215+
RETURN count(foo) > 0
1216+
$$) as (c agtype);
1217+
SELECT * FROM cypher('issue_1399', $$
1218+
MATCH (foo)
1219+
WHERE exists((foo)-[:BAR]->())
1220+
RETURN count(foo) > 0
1221+
$$) as (c agtype);
1222+
SELECT * FROM cypher('issue_1399', $$
1223+
MATCH (foo)
1224+
WHERE NOT exists((foo)-[:BAR]->())
1225+
RETURN count(foo) > 0
1226+
$$) as (c agtype);
1227+
-- create 1 path
1228+
SELECT * FROM cypher('issue_1399', $$
1229+
CREATE (foo)-[:BAR]->() RETURN foo
1230+
$$) as (c agtype);
1231+
-- these should each return 1 row as it is a directed edge and
1232+
-- only one vertex can match.
1233+
SELECT * FROM cypher('issue_1399', $$
1234+
MATCH (foo)
1235+
WHERE exists((foo)-[]->())
1236+
RETURN foo
1237+
$$) as (c agtype);
1238+
SELECT * FROM cypher('issue_1399', $$
1239+
MATCH (foo)
1240+
WHERE NOT exists((foo)-[]->())
1241+
RETURN foo
1242+
$$) as (c agtype);
1243+
SELECT * FROM cypher('issue_1399', $$
1244+
MATCH (foo)
1245+
WHERE exists((foo)-[:BAR]->())
1246+
RETURN foo
1247+
$$) as (c agtype);
1248+
SELECT * FROM cypher('issue_1399', $$
1249+
MATCH (foo)
1250+
WHERE NOT exists((foo)-[:BAR]->())
1251+
RETURN foo
1252+
$$) as (c agtype);
1253+
-- this should return 0 rows as it can't exist - that path isn't in BAR2
1254+
SELECT * FROM cypher('issue_1399', $$
1255+
MATCH (foo)
1256+
WHERE exists((foo)-[:BAR2]->())
1257+
RETURN foo
1258+
$$) as (c agtype);
1259+
-- this should return 2 rows as they all exist
1260+
SELECT * FROM cypher('issue_1399', $$
1261+
MATCH (foo)
1262+
WHERE NOT exists((foo)-[:BAR2]->())
1263+
RETURN foo
1264+
$$) as (c agtype);
1265+
11811266
--
11821267
-- Clean up
11831268
--
11841269
SELECT drop_graph('cypher_match', true);
11851270
SELECT drop_graph('test_retrieve_var', true);
11861271
SELECT drop_graph('test_enable_containment', true);
11871272
SELECT drop_graph('issue_945', true);
1273+
SELECT drop_graph('issue_1399', true);
11881274

11891275
--
11901276
-- End

src/backend/parser/cypher_transform_entity.c

+30-23
Original file line numberDiff line numberDiff line change
@@ -114,33 +114,40 @@ transform_entity *find_variable(cypher_parsestate *cpstate, char *name)
114114
{
115115
ListCell *lc;
116116

117-
foreach (lc, cpstate->entities)
117+
/* while we have cypher_parsestates to check */
118+
while (cpstate)
118119
{
119-
transform_entity *entity = lfirst(lc);
120-
char *entity_name;
121-
122-
if (entity->type == ENT_VERTEX)
123-
{
124-
entity_name = entity->entity.node->name;
125-
}
126-
else if (entity->type == ENT_EDGE || entity->type == ENT_VLE_EDGE)
127-
{
128-
entity_name = entity->entity.rel->name;
129-
}
130-
else if (entity->type == ENT_PATH)
120+
foreach (lc, cpstate->entities)
131121
{
132-
entity_name = entity->entity.path->var_name;
133-
}
134-
else
135-
{
136-
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
137-
errmsg("unknown entity type")));
138-
}
122+
transform_entity *entity = lfirst(lc);
123+
char *entity_name = NULL;
139124

140-
if (entity_name != NULL && !strcmp(name, entity_name))
141-
{
142-
return entity;
125+
if (entity->type == ENT_VERTEX)
126+
{
127+
entity_name = entity->entity.node->name;
128+
}
129+
else if (entity->type == ENT_EDGE || entity->type == ENT_VLE_EDGE)
130+
{
131+
entity_name = entity->entity.rel->name;
132+
}
133+
else if (entity->type == ENT_PATH)
134+
{
135+
entity_name = entity->entity.path->var_name;
136+
}
137+
else
138+
{
139+
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
140+
errmsg("unknown entity type")));
141+
}
142+
143+
if (entity_name != NULL && !strcmp(name, entity_name))
144+
{
145+
return entity;
146+
}
143147
}
148+
149+
/* go up to the next parent parse state */
150+
cpstate = (cypher_parsestate*)cpstate->pstate.parentParseState;
144151
}
145152

146153
return NULL;

0 commit comments

Comments
 (0)