From ffc9869232a6c8d9a824277971bf4dcc4956bedf Mon Sep 17 00:00:00 2001 From: Dehowe Feng <8065116+dehowef@users.noreply.github.com> Date: Sat, 11 May 2024 01:35:54 +0800 Subject: [PATCH] Implement Returnless Unions in Subqueries (#1803) Added logic to support returnless unions in subqueries. Added regression tests to support this addition. Modified regression tests to capture more cases. --- regress/expected/cypher_subquery.out | 165 +++++++++++++++++---------- regress/sql/cypher_subquery.sql | 17 ++- src/backend/nodes/cypher_outfuncs.c | 1 + src/backend/parser/cypher_clause.c | 7 +- src/backend/parser/cypher_gram.y | 22 +++- src/include/nodes/cypher_nodes.h | 1 + 6 files changed, 143 insertions(+), 70 deletions(-) diff --git a/regress/expected/cypher_subquery.out b/regress/expected/cypher_subquery.out index 559f0c67b..ff5672bca 100644 --- a/regress/expected/cypher_subquery.out +++ b/regress/expected/cypher_subquery.out @@ -12,7 +12,7 @@ SELECT * FROM cypher('subquery', $$ (:person {name: "Chan", age: 45})<-[:knows]-(:person {name: "Faye", age: 25})-[:knows]-> (:person {name: "Tony", age: 34})-[:loved]->(:person {name : "Valerie", age: 33}), (:person {name: "Calvin", age: 6})-[:knows]->(:pet {name: "Hobbes"}), - (:person {name: "Charlie", age: 8})-[:knows]->(:pet {name : "Snoopy"}), + (:person {name: "Lucy", age: 8})<-[:knows]-(:person {name: "Charlie", age: 8})-[:knows]->(:pet {name : "Snoopy"}), (:pet {name: "Odie"})<-[:knows]-(:person {name: "Jon", age: 29})-[:knows]->(:pet {name: "Garfield"}) $$) AS (result agtype); result @@ -29,13 +29,14 @@ SELECT * FROM cypher('subquery', $$ MATCH (a) RETURN (a) $$) AS (result agtype); {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex {"id": 844424930131974, "label": "person", "properties": {"age": 33, "name": "Valerie"}}::vertex {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Lucy"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex {"id": 1688849860263937, "label": "pet", "properties": {"name": "Hobbes"}}::vertex {"id": 1688849860263938, "label": "pet", "properties": {"name": "Snoopy"}}::vertex {"id": 1688849860263939, "label": "pet", "properties": {"name": "Odie"}}::vertex {"id": 1688849860263940, "label": "pet", "properties": {"name": "Garfield"}}::vertex -(13 rows) +(14 rows) SELECT * FROM cypher('subquery', $$ MATCH (a:person) WHERE EXISTS {(a:person)-[]->(:pet)} @@ -43,8 +44,8 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) result ------------------------------------------------------------------------------------------------- {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex (3 rows) --trying to use b when not defined, should create pattern @@ -54,8 +55,8 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) result ------------------------------------------------------------------------------------------------- {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex (3 rows) --query inside @@ -65,8 +66,8 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) result ------------------------------------------------------------------------------------------------- {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex (3 rows) --repeat variable in match @@ -134,18 +135,36 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) ERROR: could not find rte for c LINE 5: RETURN c ^ ---union, no returns, not yet implemented, should error out +--union, no returns SELECT * FROM cypher('subquery', $$ MATCH (a:person) WHERE EXISTS { MATCH (a:person)-[]->(b:pet) WHERE a.name = 'Charlie' UNION - MATCH (c:person)-[]->(d:person) + MATCH (a:person)-[]->(c:person) } RETURN (a) $$) AS (result agtype); -ERROR: Subquery UNION without returns not yet implemented -LINE 5: UNION - ^ + result +--------------------------------------------------------------------------------------------------- + {"id": 844424930131969, "label": "person", "properties": {"age": 32, "name": "Briggite"}}::vertex + {"id": 844424930131972, "label": "person", "properties": {"age": 25, "name": "Faye"}}::vertex + {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex +(4 rows) + +--union, mismatched number of return columns for returns +SELECT * FROM cypher('subquery', $$ MATCH (a:person) + WHERE EXISTS { + MATCH (a:person)-[]->(b:pet) + RETURN a, b + UNION + MATCH (a:person)-[]->(c:person) + RETURN c + } + RETURN (a) $$) AS (result agtype); +ERROR: each UNION query must have the same number of columns +LINE 7: RETURN c + ^ --union, only one has return, should fail SELECT * FROM cypher('subquery', $$ MATCH (a:person) WHERE EXISTS { @@ -180,9 +199,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex {"id": 844424930131974, "label": "person", "properties": {"age": 33, "name": "Valerie"}}::vertex {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex -(9 rows) + {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Lucy"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex +(10 rows) --nesting same var multiple layers SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -218,13 +238,14 @@ SELECT * FROM cypher('subquery', $$ MATCH (a) {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex {"id": 844424930131974, "label": "person", "properties": {"age": 33, "name": "Valerie"}}::vertex {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Lucy"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex {"id": 1688849860263937, "label": "pet", "properties": {"name": "Hobbes"}}::vertex {"id": 1688849860263938, "label": "pet", "properties": {"name": "Snoopy"}}::vertex {"id": 1688849860263939, "label": "pet", "properties": {"name": "Odie"}}::vertex {"id": 1688849860263940, "label": "pet", "properties": {"name": "Garfield"}}::vertex -(13 rows) +(14 rows) --nesting, accessing indirection in outer scope SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -246,9 +267,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex {"id": 844424930131974, "label": "person", "properties": {"age": 33, "name": "Valerie"}}::vertex {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex -(9 rows) + {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Lucy"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex +(10 rows) --nesting, accessing var 2+ levels up SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -286,9 +308,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex {"id": 844424930131974, "label": "person", "properties": {"age": 33, "name": "Valerie"}}::vertex {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex -(9 rows) + {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Lucy"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex +(10 rows) --EXISTS outside of WHERE SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -303,9 +326,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex | false {"id": 844424930131974, "label": "person", "properties": {"age": 33, "name": "Valerie"}}::vertex | false {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex | true - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex | true - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex | true -(9 rows) + {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Lucy"}}::vertex | false + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex | true + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex | true +(10 rows) --Var doesnt exist in outside scope, should fail SELECT * FROM cypher('subquery', $$ RETURN 1, @@ -338,9 +362,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) 0 0 1 + 0 1 2 -(9 rows) +(10 rows) --count pattern with WHERE SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -358,18 +383,20 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) 1 0 0 -(9 rows) + 0 +(10 rows) --solo match in where SELECT * FROM cypher('subquery', $$ MATCH (a:person) WHERE COUNT{MATCH (a:person)-[]-()} > 1 RETURN a $$) AS (result agtype); - result ------------------------------------------------------------------------------------------------ + result +------------------------------------------------------------------------------------------------- {"id": 844424930131972, "label": "person", "properties": {"age": 25, "name": "Faye"}}::vertex {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex -(3 rows) + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex +(4 rows) --match where person has more than one pet SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -377,25 +404,26 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) RETURN a $$) AS (result agtype); result ---------------------------------------------------------------------------------------------- - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex (1 row) --match on labels SELECT * FROM cypher('subquery', $$ MATCH (a:person) WHERE COUNT{MATCH (a:person)-[:knows]-()} > 1 RETURN a $$) AS (result agtype); - result ------------------------------------------------------------------------------------------------ + result +------------------------------------------------------------------------------------------------- {"id": 844424930131972, "label": "person", "properties": {"age": 25, "name": "Faye"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex -(2 rows) + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex +(3 rows) SELECT * FROM cypher('subquery', $$ MATCH (a:person) WHERE COUNT{MATCH (a:person)-[:knows]-(:pet)} > 1 RETURN a $$) AS (result agtype); result ---------------------------------------------------------------------------------------------- - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex (1 row) SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -420,7 +448,8 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) 1 1 1 -(9 rows) + 1 +(10 rows) --match return in where SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -443,7 +472,8 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) 1 1 1 -(9 rows) + 1 +(10 rows) --match where return SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -460,9 +490,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) "Tony" | 34 | 1 "Valerie" | 33 | 1 "Calvin" | 6 | 0 + "Lucy" | 8 | 0 "Charlie" | 8 | 0 "Jon" | 29 | 1 -(9 rows) +(10 rows) --counting number of relationships per node SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -476,9 +507,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) "Tony" | 2 "Valerie" | 1 "Calvin" | 1 - "Charlie" | 1 + "Lucy" | 1 + "Charlie" | 2 "Jon" | 2 -(9 rows) +(10 rows) --nested counts SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -496,9 +528,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) "Tony" | 0 "Valerie" | 0 "Calvin" | 1 + "Lucy" | 1 "Charlie" | 1 "Jon" | 0 -(9 rows) +(10 rows) --incorrect variable reference SELECT * FROM cypher('subquery', $$ MATCH (a:person) @@ -531,9 +564,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) "Tony" | 0 "Valerie" | 0 "Calvin" | 1 + "Lucy" | 0 "Charlie" | 1 "Jon" | 1 -(9 rows) +(10 rows) -- -- expression tree walker additional tests. want to test the nesting capabilties of the expr tree walker @@ -559,8 +593,8 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) {"id": 844424930131972, "label": "person", "properties": {"age": 25, "name": "Faye"}}::vertex {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex (6 rows) -- Nested BoolExpr @@ -569,9 +603,10 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) WHERE a.name = 'Charlie'} AND EXISTS {(a:person)-[]->(:person)} RETURN (a) $$) AS (result agtype); - result --------- -(0 rows) + result +------------------------------------------------------------------------------------------------- + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex +(1 row) -- CaseExpr -- subqueries in WHEN statement in RETURN @@ -590,11 +625,12 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person)-[]->(b) "Faye" | "Chan" | "There is a relationship" "Faye" | "Tony" | "There is a relationship" "Tony" | "Valerie" | "There is LOVE!!!!!!" + "Charlie" | "Lucy" | "There is a relationship" "Calvin" | "Hobbes" | "There is a relationship" "Charlie" | "Snoopy" | "There is a relationship" "Jon" | "Odie" | "There is a relationship" "Jon" | "Garfield" | "There is a relationship" -(8 rows) +(9 rows) -- subqueries in THEN, WHERE SELECT * FROM cypher('subquery', $$ MATCH (a:person)-[]->(b) @@ -607,8 +643,8 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person)-[]->(b) result ----------------------------------------------------------------------------------------------- {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex (3 rows) -- nested in another exists @@ -633,8 +669,9 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person)-[]->(b) result ------------------------------------------------------------------------------------------------- {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex -(2 rows) + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex +(3 rows) --coalesce, nested in return SELECT * FROM cypher('subquery', $$ MATCH (a:person)-[]->(b) @@ -674,13 +711,14 @@ SELECT * FROM cypher('subquery', $$MATCH (a) {"id": 844424930131973, "label": "person", "properties": {"age": 34, "name": "Tony"}}::vertex | false {"id": 844424930131974, "label": "person", "properties": {"age": 33, "name": "Valerie"}}::vertex | false {"id": 844424930131975, "label": "person", "properties": {"age": 6, "name": "Calvin"}}::vertex | false - {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex | false - {"id": 844424930131977, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex | false + {"id": 844424930131976, "label": "person", "properties": {"age": 8, "name": "Lucy"}}::vertex | false + {"id": 844424930131977, "label": "person", "properties": {"age": 8, "name": "Charlie"}}::vertex | false + {"id": 844424930131978, "label": "person", "properties": {"age": 29, "name": "Jon"}}::vertex | false {"id": 1688849860263937, "label": "pet", "properties": {"name": "Hobbes"}}::vertex | true {"id": 1688849860263938, "label": "pet", "properties": {"name": "Snoopy"}}::vertex | true {"id": 1688849860263939, "label": "pet", "properties": {"name": "Odie"}}::vertex | true {"id": 1688849860263940, "label": "pet", "properties": {"name": "Garfield"}}::vertex | true -(13 rows) +(14 rows) -- map projection --where @@ -706,13 +744,14 @@ SELECT * FROM cypher('subquery', $$MATCH (a) "Tony" | {"a": false} "Valerie" | {"a": false} "Calvin" | {"a": false} + "Lucy" | {"a": false} "Charlie" | {"a": false} "Jon" | {"a": false} "Hobbes" | {"a": false} "Snoopy" | {"a": false} "Odie" | {"a": false} "Garfield" | {"a": false} -(13 rows) +(14 rows) --lists --list diff --git a/regress/sql/cypher_subquery.sql b/regress/sql/cypher_subquery.sql index 1f10435ad..43c4db539 100644 --- a/regress/sql/cypher_subquery.sql +++ b/regress/sql/cypher_subquery.sql @@ -8,7 +8,7 @@ SELECT * FROM cypher('subquery', $$ (:person {name: "Chan", age: 45})<-[:knows]-(:person {name: "Faye", age: 25})-[:knows]-> (:person {name: "Tony", age: 34})-[:loved]->(:person {name : "Valerie", age: 33}), (:person {name: "Calvin", age: 6})-[:knows]->(:pet {name: "Hobbes"}), - (:person {name: "Charlie", age: 8})-[:knows]->(:pet {name : "Snoopy"}), + (:person {name: "Lucy", age: 8})<-[:knows]-(:person {name: "Charlie", age: 8})-[:knows]->(:pet {name : "Snoopy"}), (:pet {name: "Odie"})<-[:knows]-(:person {name: "Jon", age: 29})-[:knows]->(:pet {name: "Garfield"}) $$) AS (result agtype); @@ -73,13 +73,24 @@ SELECT * FROM cypher('subquery', $$ MATCH (a:person) } RETURN (a) $$) AS (result agtype); ---union, no returns, not yet implemented, should error out +--union, no returns SELECT * FROM cypher('subquery', $$ MATCH (a:person) WHERE EXISTS { MATCH (a:person)-[]->(b:pet) WHERE a.name = 'Charlie' UNION - MATCH (c:person)-[]->(d:person) + MATCH (a:person)-[]->(c:person) + } + RETURN (a) $$) AS (result agtype); + +--union, mismatched number of return columns for returns +SELECT * FROM cypher('subquery', $$ MATCH (a:person) + WHERE EXISTS { + MATCH (a:person)-[]->(b:pet) + RETURN a, b + UNION + MATCH (a:person)-[]->(c:person) + RETURN c } RETURN (a) $$) AS (result agtype); diff --git a/src/backend/nodes/cypher_outfuncs.c b/src/backend/nodes/cypher_outfuncs.c index 662d78475..29dc2ce23 100644 --- a/src/backend/nodes/cypher_outfuncs.c +++ b/src/backend/nodes/cypher_outfuncs.c @@ -105,6 +105,7 @@ void out_cypher_return(StringInfo str, const ExtensibleNode *node) WRITE_NODE_FIELD(limit); WRITE_BOOL_FIELD(all_or_distinct); + WRITE_BOOL_FIELD(returnless_union); WRITE_ENUM_FIELD(op, SetOperation); WRITE_NODE_FIELD(larg); WRITE_NODE_FIELD(rarg); diff --git a/src/backend/parser/cypher_clause.c b/src/backend/parser/cypher_clause.c index d2df8fc86..1cbde9a24 100644 --- a/src/backend/parser/cypher_clause.c +++ b/src/backend/parser/cypher_clause.c @@ -800,6 +800,8 @@ transform_cypher_union_tree(cypher_parsestate *cpstate, cypher_clause *clause, /* * Extract a list of the non-junk TLEs for upper-level processing. */ + + //mechanism to check for top level query list items here? if (targetlist) { *targetlist = NIL; @@ -880,8 +882,11 @@ transform_cypher_union_tree(cypher_parsestate *cpstate, cypher_clause *clause, /* * Verify that the two children have the same number of non-junk * columns, and determine the types of the merged output columns. + * If we are in a returnless subquery, we do not care about the columns + * matching, because they are not relevant to the end result. */ - if (list_length(ltargetlist) != list_length(rtargetlist)) + if (list_length(ltargetlist) != list_length(rtargetlist) && + self->returnless_union == false) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y index a0a6c35f6..d66371934 100644 --- a/src/backend/parser/cypher_gram.y +++ b/src/backend/parser/cypher_gram.y @@ -242,6 +242,10 @@ 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, List *rarg); +static Node *make_subquery_returnless_set_op(SetOperation op, + bool all_or_distinct, + List *larg, + List *rarg); // VLE static cypher_relationship *build_VLE_relation(List *left_arg, @@ -647,9 +651,7 @@ subquery_stmt_no_return: } | subquery_stmt_no_return UNION all_or_distinct subquery_stmt_no_return { - ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("Subquery UNION without returns not yet implemented"), - ag_scanner_errposition(@2, scanner))); + $$ = list_make1(make_subquery_returnless_set_op(SETOP_UNION, $3, $1, $4)); } ; @@ -2972,6 +2974,20 @@ static Node *make_set_op(SetOperation op, bool all_or_distinct, List *larg, return (Node *) n; } +/*set operation function node to make a returnless set op node for subqueries*/ +static Node *make_subquery_returnless_set_op(SetOperation op, bool all_or_distinct, List *larg, + List *rarg) +{ + cypher_return *n = make_ag_node(cypher_return); + + n->op = op; + n->all_or_distinct = all_or_distinct; + n->returnless_union = true; + n->larg = (List *) larg; + n->rarg = (List *) rarg; + return (Node *) n; +} + /* check if A_Expr is a comparison expression */ static bool is_A_Expr_a_comparison_operation(cypher_comparison_aexpr *a) { diff --git a/src/include/nodes/cypher_nodes.h b/src/include/nodes/cypher_nodes.h index a64f5a003..5234417a9 100644 --- a/src/include/nodes/cypher_nodes.h +++ b/src/include/nodes/cypher_nodes.h @@ -58,6 +58,7 @@ typedef struct cypher_return Node *limit; bool all_or_distinct; + bool returnless_union; SetOperation op; List *larg; /* lefthand argument of the unions */ List *rarg; /*righthand argument of the unions */