Skip to content

Commit 6a5298e

Browse files
committed
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 207513c commit 6a5298e

File tree

3 files changed

+288
-23
lines changed

3 files changed

+288
-23
lines changed

Diff for: regress/expected/cypher_match.out

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

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

28903051
(1 row)
28913052

3053+
SELECT drop_graph('issue_1399', true);
3054+
NOTICE: drop cascades to 3 other objects
3055+
DETAIL: drop cascades to table issue_1399._ag_label_vertex
3056+
drop cascades to table issue_1399._ag_label_edge
3057+
drop cascades to table issue_1399."BAR"
3058+
NOTICE: graph "issue_1399" has been dropped
3059+
drop_graph
3060+
------------
3061+
3062+
(1 row)
3063+
28923064
--
28933065
-- End
28943066
--

Diff for: regress/sql/cypher_match.sql

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

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

11931279
--
11941280
-- End

Diff for: 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)