Skip to content

Commit a282bcd

Browse files
authored
Implement EXISTS subquery for CASE (#1345) (#1382)
Modified logic of EXISTS to wrap itself in a agtype cast. This allows EXISTS to work with in CASE statements, among other implementations that may require an agtype. Also renamed the wrapper function to be more generic to denote its usage in other implementations if need be. Added regression tests for EXISTS
1 parent 51ca825 commit a282bcd

File tree

3 files changed

+134
-24
lines changed

3 files changed

+134
-24
lines changed

regress/expected/expr.out

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6398,8 +6398,8 @@ SELECT * FROM cypher('case_statement', $$
63986398
WHEN 1 THEN count(*)
63996399
ELSE 'not count'
64006400
END
6401-
$$ ) AS (j agtype, case_statement agtype);
6402-
j | case_statement
6401+
$$ ) AS (n agtype, case_statement agtype);
6402+
n | case_statement
64036403
------------------------------------------------------------------------------------------------+----------------
64046404
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count"
64056405
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count"
@@ -6416,8 +6416,8 @@ SELECT * FROM cypher('case_statement', $$
64166416
WHEN 1 THEN count(*)
64176417
ELSE 'not count'
64186418
END
6419-
$$ ) AS (j agtype, case_statement agtype);
6420-
j | case_statement
6419+
$$ ) AS (n agtype, case_statement agtype);
6420+
n | case_statement
64216421
------------------------------------------------------------------------------------------------+----------------
64226422
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count"
64236423
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count"
@@ -6434,8 +6434,8 @@ SELECT * FROM cypher('case_statement', $$
64346434
WHEN 1 THEN count(n)
64356435
ELSE 'not count'
64366436
END
6437-
$$ ) AS (j agtype, case_statement agtype);
6438-
j | case_statement
6437+
$$ ) AS (n agtype, case_statement agtype);
6438+
n | case_statement
64396439
------------------------------------------------------------------------------------------------+----------------
64406440
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count"
64416441
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count"
@@ -6452,8 +6452,8 @@ SELECT * FROM cypher('case_statement', $$
64526452
WHEN 1 THEN count(n)
64536453
ELSE 'not count'
64546454
END
6455-
$$ ) AS (j agtype, case_statement agtype);
6456-
j | case_statement
6455+
$$ ) AS (n agtype, case_statement agtype);
6456+
n | case_statement
64576457
------------------------------------------------------------------------------------------------+----------------
64586458
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count"
64596459
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count"
@@ -6470,8 +6470,8 @@ SELECT * FROM cypher('case_statement', $$
64706470
WHEN 1 THEN count(1)
64716471
ELSE 'not count'
64726472
END
6473-
$$ ) AS (j agtype, case_statement agtype);
6474-
j | case_statement
6473+
$$ ) AS (n agtype, case_statement agtype);
6474+
n | case_statement
64756475
------------------------------------------------------------------------------------------------+----------------
64766476
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count"
64776477
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count"
@@ -6488,8 +6488,8 @@ SELECT * FROM cypher('case_statement', $$
64886488
WHEN 1 THEN count(1)
64896489
ELSE 'not count'
64906490
END
6491-
$$ ) AS (j agtype, case_statement agtype);
6492-
j | case_statement
6491+
$$ ) AS (n agtype, case_statement agtype);
6492+
n | case_statement
64936493
------------------------------------------------------------------------------------------------+----------------
64946494
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count"
64956495
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count"
@@ -6499,6 +6499,72 @@ $$ ) AS (j agtype, case_statement agtype);
64996499
{"id": 281474976710662, "label": "", "properties": {"i": {}, "j": {"i": 1}, "id": 6}}::vertex | "not count"
65006500
(6 rows)
65016501

6502+
--CASE with EXISTS()
6503+
--exists(n.property)
6504+
SELECT * FROM cypher('case_statement', $$
6505+
MATCH (n)
6506+
RETURN n, CASE exists(n.j)
6507+
WHEN true THEN 'property j exists'
6508+
ELSE 'property j does not exist'
6509+
END
6510+
$$ ) AS (n agtype, case_statement agtype);
6511+
n | case_statement
6512+
------------------------------------------------------------------------------------------------+-----------------------------
6513+
{"id": 281474976710657, "label": "", "properties": {"i": 1, "id": 1}}::vertex | "property j does not exist"
6514+
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "property j exists"
6515+
{"id": 281474976710659, "label": "", "properties": {"i": 0, "j": 1, "id": 3}}::vertex | "property j exists"
6516+
{"id": 281474976710660, "label": "", "properties": {"i": true, "j": false, "id": 4}}::vertex | "property j exists"
6517+
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "property j exists"
6518+
{"id": 281474976710662, "label": "", "properties": {"i": {}, "j": {"i": 1}, "id": 6}}::vertex | "property j exists"
6519+
(6 rows)
6520+
6521+
--CASE evaluates to boolean true, is not a boolean, should hit ELSE
6522+
SELECT * FROM cypher('case_statement', $$
6523+
MATCH (n)
6524+
RETURN n, CASE exists(n.j)
6525+
WHEN 1 THEN 'should not output me'
6526+
ELSE '1 is not a boolean'
6527+
END
6528+
$$ ) AS (n agtype, case_statement agtype);
6529+
n | case_statement
6530+
------------------------------------------------------------------------------------------------+----------------------
6531+
{"id": 281474976710657, "label": "", "properties": {"i": 1, "id": 1}}::vertex | "1 is not a boolean"
6532+
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "1 is not a boolean"
6533+
{"id": 281474976710659, "label": "", "properties": {"i": 0, "j": 1, "id": 3}}::vertex | "1 is not a boolean"
6534+
{"id": 281474976710660, "label": "", "properties": {"i": true, "j": false, "id": 4}}::vertex | "1 is not a boolean"
6535+
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "1 is not a boolean"
6536+
{"id": 281474976710662, "label": "", "properties": {"i": {}, "j": {"i": 1}, "id": 6}}::vertex | "1 is not a boolean"
6537+
(6 rows)
6538+
6539+
--exists in WHEN, vacuously false because exists(n.j) evaluates to a boolean, n is a vertex
6540+
SELECT * FROM cypher('case_statement', $$
6541+
MATCH (n)
6542+
RETURN n, CASE n
6543+
WHEN exists(n.j) THEN 'should not output me'
6544+
ELSE 'n is a vertex, not a boolean'
6545+
END
6546+
$$ ) AS (j agtype, case_statement agtype);
6547+
j | case_statement
6548+
------------------------------------------------------------------------------------------------+--------------------------------
6549+
{"id": 281474976710657, "label": "", "properties": {"i": 1, "id": 1}}::vertex | "n is a vertex, not a boolean"
6550+
{"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "n is a vertex, not a boolean"
6551+
{"id": 281474976710659, "label": "", "properties": {"i": 0, "j": 1, "id": 3}}::vertex | "n is a vertex, not a boolean"
6552+
{"id": 281474976710660, "label": "", "properties": {"i": true, "j": false, "id": 4}}::vertex | "n is a vertex, not a boolean"
6553+
{"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "n is a vertex, not a boolean"
6554+
{"id": 281474976710662, "label": "", "properties": {"i": {}, "j": {"i": 1}, "id": 6}}::vertex | "n is a vertex, not a boolean"
6555+
(6 rows)
6556+
6557+
--exists(*) (should fail)
6558+
SELECT * FROM cypher('case_statement', $$
6559+
MATCH (n)
6560+
RETURN n, CASE n.j
6561+
WHEN 1 THEN exists(*)
6562+
ELSE 'not count'
6563+
END
6564+
$$ ) AS (j agtype, case_statement agtype);
6565+
ERROR: syntax error at or near "*"
6566+
LINE 4: WHEN 1 THEN exists(*)
6567+
^
65026568
-- RETURN * and (u)--(v) optional forms
65036569
SELECT create_graph('opt_forms');
65046570
NOTICE: graph "opt_forms" has been created

regress/sql/expr.sql

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2682,7 +2682,7 @@ SELECT * FROM cypher('case_statement', $$
26822682
WHEN 1 THEN count(*)
26832683
ELSE 'not count'
26842684
END
2685-
$$ ) AS (j agtype, case_statement agtype);
2685+
$$ ) AS (n agtype, case_statement agtype);
26862686

26872687
--concatenated
26882688
SELECT * FROM cypher('case_statement', $$
@@ -2691,7 +2691,7 @@ SELECT * FROM cypher('case_statement', $$
26912691
WHEN 1 THEN count(*)
26922692
ELSE 'not count'
26932693
END
2694-
$$ ) AS (j agtype, case_statement agtype);
2694+
$$ ) AS (n agtype, case_statement agtype);
26952695

26962696
--count(n)
26972697
SELECT * FROM cypher('case_statement', $$
@@ -2700,7 +2700,7 @@ SELECT * FROM cypher('case_statement', $$
27002700
WHEN 1 THEN count(n)
27012701
ELSE 'not count'
27022702
END
2703-
$$ ) AS (j agtype, case_statement agtype);
2703+
$$ ) AS (n agtype, case_statement agtype);
27042704

27052705
--concatenated
27062706
SELECT * FROM cypher('case_statement', $$
@@ -2709,7 +2709,7 @@ SELECT * FROM cypher('case_statement', $$
27092709
WHEN 1 THEN count(n)
27102710
ELSE 'not count'
27112711
END
2712-
$$ ) AS (j agtype, case_statement agtype);
2712+
$$ ) AS (n agtype, case_statement agtype);
27132713

27142714
--count(1)
27152715
SELECT * FROM cypher('case_statement', $$
@@ -2718,7 +2718,7 @@ SELECT * FROM cypher('case_statement', $$
27182718
WHEN 1 THEN count(1)
27192719
ELSE 'not count'
27202720
END
2721-
$$ ) AS (j agtype, case_statement agtype);
2721+
$$ ) AS (n agtype, case_statement agtype);
27222722

27232723
--concatenated
27242724
SELECT * FROM cypher('case_statement', $$
@@ -2727,8 +2727,48 @@ SELECT * FROM cypher('case_statement', $$
27272727
WHEN 1 THEN count(1)
27282728
ELSE 'not count'
27292729
END
2730+
$$ ) AS (n agtype, case_statement agtype);
2731+
2732+
--CASE with EXISTS()
2733+
2734+
--exists(n.property)
2735+
SELECT * FROM cypher('case_statement', $$
2736+
MATCH (n)
2737+
RETURN n, CASE exists(n.j)
2738+
WHEN true THEN 'property j exists'
2739+
ELSE 'property j does not exist'
2740+
END
2741+
$$ ) AS (n agtype, case_statement agtype);
2742+
2743+
--CASE evaluates to boolean true, is not a boolean, should hit ELSE
2744+
SELECT * FROM cypher('case_statement', $$
2745+
MATCH (n)
2746+
RETURN n, CASE exists(n.j)
2747+
WHEN 1 THEN 'should not output me'
2748+
ELSE '1 is not a boolean'
2749+
END
2750+
$$ ) AS (n agtype, case_statement agtype);
2751+
2752+
--exists in WHEN, vacuously false because exists(n.j) evaluates to a boolean, n is a vertex
2753+
SELECT * FROM cypher('case_statement', $$
2754+
MATCH (n)
2755+
RETURN n, CASE n
2756+
WHEN exists(n.j) THEN 'should not output me'
2757+
ELSE 'n is a vertex, not a boolean'
2758+
END
27302759
$$ ) AS (j agtype, case_statement agtype);
27312760

2761+
--exists(*) (should fail)
2762+
SELECT * FROM cypher('case_statement', $$
2763+
MATCH (n)
2764+
RETURN n, CASE n.j
2765+
WHEN 1 THEN exists(*)
2766+
ELSE 'not count'
2767+
END
2768+
$$ ) AS (j agtype, case_statement agtype);
2769+
2770+
2771+
27322772

27332773
-- RETURN * and (u)--(v) optional forms
27342774
SELECT create_graph('opt_forms');

src/backend/parser/cypher_gram.y

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ static Node *make_typecast_expr(Node *expr, char *typecast, int location);
227227
static Node *make_function_expr(List *func_name, List *exprs, int location);
228228
static Node *make_star_function_expr(List *func_name, List *exprs, int location);
229229
static Node *make_distinct_function_expr(List *func_name, List *exprs, int location);
230-
static FuncCall *wrap_pg_funccall_to_agtype(Node* fnode, char *type, int location);
230+
static FuncCall *node_to_agtype(Node* fnode, char *type, int location);
231231

232232
// setops
233233
static Node *make_set_op(SetOperation op, bool all_or_distinct, List *larg,
@@ -1728,12 +1728,16 @@ expr_func_subexpr:
17281728
n->operName = NIL;
17291729
n->subselect = (Node *) sub;
17301730
n->location = @1;
1731-
$$ = (Node *) n;
1731+
$$ = (Node *)node_to_agtype((Node *)n, "boolean", @1);
17321732
}
17331733
| EXISTS '(' property_value ')'
17341734
{
1735-
$$ = make_function_expr(list_make1(makeString("exists")),
1735+
FuncCall *n;
1736+
n = makeFuncCall(list_make1(makeString("exists")),
17361737
list_make1($3), @2);
1738+
1739+
$$ = (Node *)node_to_agtype((Node *)n, "boolean", @2);
1740+
17371741
}
17381742
;
17391743

@@ -2266,7 +2270,7 @@ static Node *make_function_expr(List *func_name, List *exprs, int location)
22662270
fnode = makeFuncCall(funcname, exprs, location);
22672271

22682272
/* build the cast to wrap the function call to return agtype. */
2269-
fnode = wrap_pg_funccall_to_agtype((Node *)fnode, "integer", location);
2273+
fnode = node_to_agtype((Node *)fnode, "integer", location);
22702274

22712275
return (Node *)fnode;
22722276
}
@@ -2322,7 +2326,7 @@ static Node *make_star_function_expr(List *func_name, List *exprs, int location)
23222326
fnode->agg_star = true;
23232327

23242328
/* build the cast to wrap the function call to return agtype. */
2325-
fnode = wrap_pg_funccall_to_agtype((Node *)fnode, "integer", location);
2329+
fnode = node_to_agtype((Node *)fnode, "integer", location);
23262330

23272331
return (Node *)fnode;
23282332
}
@@ -2380,7 +2384,7 @@ static Node *make_distinct_function_expr(List *func_name, List *exprs, int locat
23802384
fnode->agg_distinct = true;
23812385

23822386
/* build the cast to wrap the function call to return agtype. */
2383-
fnode = wrap_pg_funccall_to_agtype((Node *)fnode, "integer", location);
2387+
fnode = node_to_agtype((Node *)fnode, "integer", location);
23842388
return (Node *)fnode;
23852389
}
23862390
else
@@ -2411,7 +2415,7 @@ static Node *make_distinct_function_expr(List *func_name, List *exprs, int locat
24112415
* helper function to wrap pg_function in the appropiate typecast function to
24122416
* interface with AGE components
24132417
*/
2414-
static FuncCall *wrap_pg_funccall_to_agtype(Node * fnode, char *type, int location)
2418+
static FuncCall *node_to_agtype(Node * fnode, char *type, int location)
24152419
{
24162420
List *funcname = list_make1(makeString("ag_catalog"));
24172421

0 commit comments

Comments
 (0)