Skip to content

Commit

Permalink
Implement Returnless Unions in Subqueries (apache#1803)
Browse files Browse the repository at this point in the history
Added logic to support returnless unions in subqueries.

Added regression tests to support this addition. Modified
regression tests to capture more cases.
  • Loading branch information
dehowef authored May 10, 2024
1 parent 1e02000 commit ffc9869
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 70 deletions.
165 changes: 102 additions & 63 deletions regress/expected/cypher_subquery.out

Large diffs are not rendered by default.

17 changes: 14 additions & 3 deletions regress/sql/cypher_subquery.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions src/backend/nodes/cypher_outfuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion src/backend/parser/cypher_clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand Down
22 changes: 19 additions & 3 deletions src/backend/parser/cypher_gram.y
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));
}
;

Expand Down Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions src/include/nodes/cypher_nodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit ffc9869

Please sign in to comment.