diff --git a/regress/expected/expr.out b/regress/expected/expr.out index d72572286..43b40807f 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -6514,8 +6514,8 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(*) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); - j | case_statement +$$ ) AS (n agtype, case_statement agtype); + n | case_statement ------------------------------------------------------------------------------------------------+---------------- {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count" {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count" @@ -6532,8 +6532,8 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(*) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); - j | case_statement +$$ ) AS (n agtype, case_statement agtype); + n | case_statement ------------------------------------------------------------------------------------------------+---------------- {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count" {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count" @@ -6550,8 +6550,8 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(n) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); - j | case_statement +$$ ) AS (n agtype, case_statement agtype); + n | case_statement ------------------------------------------------------------------------------------------------+---------------- {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count" {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count" @@ -6568,8 +6568,8 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(n) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); - j | case_statement +$$ ) AS (n agtype, case_statement agtype); + n | case_statement ------------------------------------------------------------------------------------------------+---------------- {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count" {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count" @@ -6586,8 +6586,8 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(1) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); - j | case_statement +$$ ) AS (n agtype, case_statement agtype); + n | case_statement ------------------------------------------------------------------------------------------------+---------------- {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count" {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count" @@ -6604,8 +6604,8 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(1) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); - j | case_statement +$$ ) AS (n agtype, case_statement agtype); + n | case_statement ------------------------------------------------------------------------------------------------+---------------- {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "not count" {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "not count" @@ -6615,6 +6615,72 @@ $$ ) AS (j agtype, case_statement agtype); {"id": 281474976710662, "label": "", "properties": {"i": {}, "j": {"i": 1}, "id": 6}}::vertex | "not count" (6 rows) +--CASE with EXISTS() +--exists(n.property) +SELECT * FROM cypher('case_statement', $$ + MATCH (n) + RETURN n, CASE exists(n.j) + WHEN true THEN 'property j exists' + ELSE 'property j does not exist' + END +$$ ) AS (n agtype, case_statement agtype); + n | case_statement +------------------------------------------------------------------------------------------------+----------------------------- + {"id": 281474976710657, "label": "", "properties": {"i": 1, "id": 1}}::vertex | "property j does not exist" + {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "property j exists" + {"id": 281474976710659, "label": "", "properties": {"i": 0, "j": 1, "id": 3}}::vertex | "property j exists" + {"id": 281474976710660, "label": "", "properties": {"i": true, "j": false, "id": 4}}::vertex | "property j exists" + {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "property j exists" + {"id": 281474976710662, "label": "", "properties": {"i": {}, "j": {"i": 1}, "id": 6}}::vertex | "property j exists" +(6 rows) + +--CASE evaluates to boolean true, is not a boolean, should hit ELSE +SELECT * FROM cypher('case_statement', $$ + MATCH (n) + RETURN n, CASE exists(n.j) + WHEN 1 THEN 'should not output me' + ELSE '1 is not a boolean' + END +$$ ) AS (n agtype, case_statement agtype); + n | case_statement +------------------------------------------------------------------------------------------------+---------------------- + {"id": 281474976710657, "label": "", "properties": {"i": 1, "id": 1}}::vertex | "1 is not a boolean" + {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "1 is not a boolean" + {"id": 281474976710659, "label": "", "properties": {"i": 0, "j": 1, "id": 3}}::vertex | "1 is not a boolean" + {"id": 281474976710660, "label": "", "properties": {"i": true, "j": false, "id": 4}}::vertex | "1 is not a boolean" + {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "1 is not a boolean" + {"id": 281474976710662, "label": "", "properties": {"i": {}, "j": {"i": 1}, "id": 6}}::vertex | "1 is not a boolean" +(6 rows) + +--exists in WHEN, vacuously false because exists(n.j) evaluates to a boolean, n is a vertex +SELECT * FROM cypher('case_statement', $$ + MATCH (n) + RETURN n, CASE n + WHEN exists(n.j) THEN 'should not output me' + ELSE 'n is a vertex, not a boolean' + END +$$ ) AS (j agtype, case_statement agtype); + j | case_statement +------------------------------------------------------------------------------------------------+-------------------------------- + {"id": 281474976710657, "label": "", "properties": {"i": 1, "id": 1}}::vertex | "n is a vertex, not a boolean" + {"id": 281474976710658, "label": "", "properties": {"i": "a", "j": "b", "id": 2}}::vertex | "n is a vertex, not a boolean" + {"id": 281474976710659, "label": "", "properties": {"i": 0, "j": 1, "id": 3}}::vertex | "n is a vertex, not a boolean" + {"id": 281474976710660, "label": "", "properties": {"i": true, "j": false, "id": 4}}::vertex | "n is a vertex, not a boolean" + {"id": 281474976710661, "label": "", "properties": {"i": [], "j": [0, 1, 2], "id": 5}}::vertex | "n is a vertex, not a boolean" + {"id": 281474976710662, "label": "", "properties": {"i": {}, "j": {"i": 1}, "id": 6}}::vertex | "n is a vertex, not a boolean" +(6 rows) + +--exists(*) (should fail) +SELECT * FROM cypher('case_statement', $$ + MATCH (n) + RETURN n, CASE n.j + WHEN 1 THEN exists(*) + ELSE 'not count' + END +$$ ) AS (j agtype, case_statement agtype); +ERROR: syntax error at or near "*" +LINE 4: WHEN 1 THEN exists(*) + ^ -- RETURN * and (u)--(v) optional forms SELECT create_graph('opt_forms'); NOTICE: graph "opt_forms" has been created diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql index 0aa62a0b0..6f7c58534 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -2726,7 +2726,7 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(*) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); +$$ ) AS (n agtype, case_statement agtype); --concatenated SELECT * FROM cypher('case_statement', $$ @@ -2735,7 +2735,7 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(*) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); +$$ ) AS (n agtype, case_statement agtype); --count(n) SELECT * FROM cypher('case_statement', $$ @@ -2744,7 +2744,7 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(n) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); +$$ ) AS (n agtype, case_statement agtype); --concatenated SELECT * FROM cypher('case_statement', $$ @@ -2753,7 +2753,7 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(n) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); +$$ ) AS (n agtype, case_statement agtype); --count(1) SELECT * FROM cypher('case_statement', $$ @@ -2762,7 +2762,7 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(1) ELSE 'not count' END -$$ ) AS (j agtype, case_statement agtype); +$$ ) AS (n agtype, case_statement agtype); --concatenated SELECT * FROM cypher('case_statement', $$ @@ -2771,8 +2771,48 @@ SELECT * FROM cypher('case_statement', $$ WHEN 1 THEN count(1) ELSE 'not count' END +$$ ) AS (n agtype, case_statement agtype); + +--CASE with EXISTS() + +--exists(n.property) +SELECT * FROM cypher('case_statement', $$ + MATCH (n) + RETURN n, CASE exists(n.j) + WHEN true THEN 'property j exists' + ELSE 'property j does not exist' + END +$$ ) AS (n agtype, case_statement agtype); + +--CASE evaluates to boolean true, is not a boolean, should hit ELSE +SELECT * FROM cypher('case_statement', $$ + MATCH (n) + RETURN n, CASE exists(n.j) + WHEN 1 THEN 'should not output me' + ELSE '1 is not a boolean' + END +$$ ) AS (n agtype, case_statement agtype); + +--exists in WHEN, vacuously false because exists(n.j) evaluates to a boolean, n is a vertex +SELECT * FROM cypher('case_statement', $$ + MATCH (n) + RETURN n, CASE n + WHEN exists(n.j) THEN 'should not output me' + ELSE 'n is a vertex, not a boolean' + END $$ ) AS (j agtype, case_statement agtype); +--exists(*) (should fail) +SELECT * FROM cypher('case_statement', $$ + MATCH (n) + RETURN n, CASE n.j + WHEN 1 THEN exists(*) + ELSE 'not count' + END +$$ ) AS (j agtype, case_statement agtype); + + + -- RETURN * and (u)--(v) optional forms SELECT create_graph('opt_forms'); diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y index 6ba8cc61d..4f7fddf5d 100644 --- a/src/backend/parser/cypher_gram.y +++ b/src/backend/parser/cypher_gram.y @@ -227,7 +227,7 @@ static Node *make_typecast_expr(Node *expr, char *typecast, int location); static Node *make_function_expr(List *func_name, List *exprs, int location); static Node *make_star_function_expr(List *func_name, List *exprs, int location); static Node *make_distinct_function_expr(List *func_name, List *exprs, int location); -static FuncCall *wrap_pg_funccall_to_agtype(Node* fnode, char *type, int location); +static FuncCall *node_to_agtype(Node* fnode, char *type, int location); // setops static Node *make_set_op(SetOperation op, bool all_or_distinct, List *larg, @@ -1742,12 +1742,16 @@ expr_func_subexpr: n->operName = NIL; n->subselect = (Node *) sub; n->location = @1; - $$ = (Node *) n; + $$ = (Node *)node_to_agtype((Node *)n, "boolean", @1); } | EXISTS '(' property_value ')' { - $$ = make_function_expr(list_make1(makeString("exists")), - list_make1($3), @2); + FuncCall *n; + n = makeFuncCall(list_make1(makeString("exists")), + list_make1($3), COERCE_SQL_SYNTAX, @2); + + $$ = (Node *)node_to_agtype((Node *)n, "boolean", @2); + } ; @@ -2289,7 +2293,7 @@ static Node *make_function_expr(List *func_name, List *exprs, int location) fnode = makeFuncCall(funcname, exprs, COERCE_SQL_SYNTAX, location); /* build the cast to wrap the function call to return agtype. */ - fnode = wrap_pg_funccall_to_agtype((Node *)fnode, "integer", location); + fnode = node_to_agtype((Node *)fnode, "integer", location); return (Node *)fnode; } @@ -2345,7 +2349,7 @@ static Node *make_star_function_expr(List *func_name, List *exprs, int location) fnode->agg_star = true; /* build the cast to wrap the function call to return agtype. */ - fnode = wrap_pg_funccall_to_agtype((Node *)fnode, "integer", location); + fnode = node_to_agtype((Node *)fnode, "integer", location); return (Node *)fnode; } @@ -2403,7 +2407,7 @@ static Node *make_distinct_function_expr(List *func_name, List *exprs, int locat fnode->agg_distinct = true; /* build the cast to wrap the function call to return agtype. */ - fnode = wrap_pg_funccall_to_agtype((Node *)fnode, "integer", location); + fnode = node_to_agtype((Node *)fnode, "integer", location); return (Node *)fnode; } else @@ -2434,7 +2438,7 @@ static Node *make_distinct_function_expr(List *func_name, List *exprs, int locat * helper function to wrap pg_function in the appropiate typecast function to * interface with AGE components */ -static FuncCall *wrap_pg_funccall_to_agtype(Node * fnode, char *type, int location) +static FuncCall *node_to_agtype(Node * fnode, char *type, int location) { List *funcname = list_make1(makeString("ag_catalog"));