Skip to content

Commit cec786f

Browse files
committed
Support Pattern Comprehensions in Read Clauses
Support only in WHERE, WITH and RETURN for now... Honestly, Thought that would be harder to support.
1 parent aba7ef1 commit cec786f

File tree

4 files changed

+217
-13
lines changed

4 files changed

+217
-13
lines changed

regress/expected/cypher_match.out

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,6 +1584,97 @@ CYPHER WITH (SELECT i FROM tst LIMIT 1) as a RETURN a;
15841584
1
15851585
(1 row)
15861586

1587+
CREATE GRAPH keanu;
1588+
NOTICE: graph "keanu" has been created
1589+
create_graph
1590+
--------------
1591+
1592+
(1 row)
1593+
1594+
USE GRAPH keanu;
1595+
use_graph
1596+
-----------
1597+
1598+
(1 row)
1599+
1600+
CREATE
1601+
(keanu:Person {name: 'Keanu Reeves'}),
1602+
(johnnyMnemonic:Movie {title: 'Johnny Mnemonic', released: 1995}),
1603+
(theMatrixRevolutions:Movie {title: 'The Matrix Revolutions', released: 2003}),
1604+
(theMatrixReloaded:Movie {title: 'The Matrix Reloaded', released: 2003}),
1605+
(theReplacements:Movie {title: 'The Replacements', released: 2000}),
1606+
(theMatrix:Movie {title: 'The Matrix', released: 1999}),
1607+
(theDevilsAdvocate:Movie {title: 'The Devils Advocate', released: 1997}),
1608+
(theMatrixResurrections:Movie {title: 'The Matrix Resurrections', released: 2021}),
1609+
(keanu)-[:ACTED_IN]->(johnnyMnemonic),
1610+
(keanu)-[:ACTED_IN]->(theMatrixRevolutions),
1611+
(keanu)-[:ACTED_IN]->(theMatrixReloaded),
1612+
(keanu)-[:ACTED_IN]->(theReplacements),
1613+
(keanu)-[:ACTED_IN]->(theMatrix),
1614+
(keanu)-[:ACTED_IN]->(theDevilsAdvocate),
1615+
(keanu)-[:ACTED_IN]->(theMatrixResurrections);
1616+
--
1617+
(0 rows)
1618+
1619+
--MATCH (keanu:Person {name: 'Keanu Reeves'})
1620+
--RETURN [MATCH (keanu) WHERE 1=1 | 1] AS years;
1621+
CYPHER WITH [MATCH (keanu:Person {name: 'Keanu Reeves'})-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released] AS years
1622+
RETURN years;
1623+
years
1624+
--------------------------
1625+
[2003, 2003, 1999, 2021]
1626+
(1 row)
1627+
1628+
MATCH (keanu:Person {name: 'Keanu Reeves'})
1629+
RETURN [
1630+
MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released
1631+
] AS years;
1632+
years
1633+
--------------------------
1634+
[2003, 2003, 1999, 2021]
1635+
(1 row)
1636+
1637+
CREATE (test:Years {years: [MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released]})
1638+
RETURN test;
1639+
ERROR: cannot handle unplanned sub-select
1640+
CREATE (test:Years2);
1641+
--
1642+
(0 rows)
1643+
1644+
MATCH (test:Years2)
1645+
SET test.years = [MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released]
1646+
RETURN test;
1647+
ERROR: cannot handle unplanned sub-select
1648+
MATCH (test {years: [MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released]})
1649+
RETURN test;
1650+
test
1651+
------
1652+
(0 rows)
1653+
1654+
MATCH (test)
1655+
WHERE test.years = [MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released]
1656+
RETURN test;
1657+
test
1658+
------
1659+
(0 rows)
1660+
1661+
RETURN case when false then '' else [MATCH (keanu:Person {name: 'Keanu Reeves'})-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released] end AS years;
1662+
years
1663+
--------------------------
1664+
[2003, 2003, 1999, 2021]
1665+
(1 row)
1666+
1667+
/*
1668+
MATCH (keanu:Person {name: 'Keanu Reeves'})
1669+
RETURN [(MATCH (keanu)-[:ACTED_IN]->(b)) WHERE b.title CONTAINS 'Matrix' | b.released] AS years;
1670+
1671+
MATCH (keanu:Person {name: 'Keanu Reeves'})
1672+
RETURN [(keanu)-[]->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.released] AS years;
1673+
1674+
1675+
MATCH (keanu:Person {name: 'Keanu Reeves'})
1676+
RETURN [(:Person)-[]->(b:Movie) WHERE b.title = 'The Matrix' | b.released] AS years;
1677+
*/
15871678
--
15881679
-- Clean up
15891680
--
@@ -1611,6 +1702,20 @@ NOTICE: graph "cypher_match" has been dropped
16111702

16121703
(1 row)
16131704

1705+
DROP GRAPH keanu CASCADE;
1706+
NOTICE: drop cascades to 6 other objects
1707+
DETAIL: drop cascades to table keanu._ag_label_vertex
1708+
drop cascades to table keanu._ag_label_edge
1709+
drop cascades to table keanu.person
1710+
drop cascades to table keanu.movie
1711+
drop cascades to table keanu.acted_in
1712+
drop cascades to table keanu.years2
1713+
NOTICE: graph "keanu" has been dropped
1714+
drop_graph
1715+
------------
1716+
1717+
(1 row)
1718+
16141719
--
16151720
-- End
16161721
--

regress/sql/cypher_match.sql

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,74 @@ RETURN (SELECT COUNT(*) FROM tst);
355355
CYPHER WITH (RETURN 1) as a RETURN a;
356356
CYPHER WITH (SELECT i FROM tst LIMIT 1) as a RETURN a;
357357

358+
CREATE GRAPH keanu;
359+
USE GRAPH keanu;
360+
361+
CREATE
362+
(keanu:Person {name: 'Keanu Reeves'}),
363+
(johnnyMnemonic:Movie {title: 'Johnny Mnemonic', released: 1995}),
364+
(theMatrixRevolutions:Movie {title: 'The Matrix Revolutions', released: 2003}),
365+
(theMatrixReloaded:Movie {title: 'The Matrix Reloaded', released: 2003}),
366+
(theReplacements:Movie {title: 'The Replacements', released: 2000}),
367+
(theMatrix:Movie {title: 'The Matrix', released: 1999}),
368+
(theDevilsAdvocate:Movie {title: 'The Devils Advocate', released: 1997}),
369+
(theMatrixResurrections:Movie {title: 'The Matrix Resurrections', released: 2021}),
370+
(keanu)-[:ACTED_IN]->(johnnyMnemonic),
371+
(keanu)-[:ACTED_IN]->(theMatrixRevolutions),
372+
(keanu)-[:ACTED_IN]->(theMatrixReloaded),
373+
(keanu)-[:ACTED_IN]->(theReplacements),
374+
(keanu)-[:ACTED_IN]->(theMatrix),
375+
(keanu)-[:ACTED_IN]->(theDevilsAdvocate),
376+
(keanu)-[:ACTED_IN]->(theMatrixResurrections);
377+
378+
--MATCH (keanu:Person {name: 'Keanu Reeves'})
379+
--RETURN [MATCH (keanu) WHERE 1=1 | 1] AS years;
380+
381+
CYPHER WITH [MATCH (keanu:Person {name: 'Keanu Reeves'})-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released] AS years
382+
RETURN years;
383+
384+
MATCH (keanu:Person {name: 'Keanu Reeves'})
385+
RETURN [
386+
MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released
387+
] AS years;
388+
389+
CREATE (test:Years {years: [MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released]})
390+
RETURN test;
391+
392+
393+
CREATE (test:Years2);
394+
395+
MATCH (test:Years2)
396+
SET test.years = [MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released]
397+
RETURN test;
398+
399+
400+
MATCH (test {years: [MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released]})
401+
RETURN test;
402+
403+
MATCH (test)
404+
WHERE test.years = [MATCH (keanu)-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released]
405+
RETURN test;
406+
407+
RETURN case when false then '' else [MATCH (keanu:Person {name: 'Keanu Reeves'})-[:ACTED_IN]->(b) WHERE b.title CONTAINS 'Matrix' RETURN b.released] end AS years;
408+
409+
410+
/*
411+
MATCH (keanu:Person {name: 'Keanu Reeves'})
412+
RETURN [(MATCH (keanu)-[:ACTED_IN]->(b)) WHERE b.title CONTAINS 'Matrix' | b.released] AS years;
413+
414+
MATCH (keanu:Person {name: 'Keanu Reeves'})
415+
RETURN [(keanu)-[]->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.released] AS years;
416+
417+
418+
MATCH (keanu:Person {name: 'Keanu Reeves'})
419+
RETURN [(:Person)-[]->(b:Movie) WHERE b.title = 'The Matrix' | b.released] AS years;
420+
*/
358421
--
359422
-- Clean up
360423
--
361424
DROP GRAPH cypher_match CASCADE;
425+
DROP GRAPH keanu CASCADE;
362426

363427
--
364428
-- End

src/backend/parser/ag_scanner.l

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,8 +1297,7 @@ ag_token token;
12971297
} if (yyleng == 1)
12981298
if ('-' == yytext[0] || '=' == yytext[0] || '<' == yytext[0] ||
12991299
'>' == yytext[0] || '*' == yytext[0] || '^' == yytext[0] ||
1300-
'%' == yytext[0] || '/' == yytext[0] || '+' == yytext[0] ||
1301-
'-' == yytext[0]) {
1300+
'%' == yytext[0] || '/' == yytext[0] || '+' == yytext[0]) {
13021301
token.type = AG_TOKEN_CHAR;
13031302
token.value.c = yytext[0];
13041303
token.location = get_location();

src/backend/parser/cypher_gram.y

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17920,13 +17920,7 @@ cypher_expr_atom:
1792017920
else
1792117921
$$ = $2;
1792217922
}
17923-
| '[' anonymous_path where_clause '|' return_item_list ']' %prec '['
17924-
{
17925-
ereport(ERROR, errmsg("pattern comprehensions are not yet implemented"));
17926-
17927-
$$ = NULL;
17928-
}
17929-
| '[' ColId IN cypher_a_expr cypher_where_opt '|' return_item ']' %prec '['
17923+
/*| '[' ColId IN cypher_a_expr cypher_where_opt '|' return_item ']' %prec '['
1793017924
{
1793117925
ereport(ERROR, errmsg("list comprehensions are not yet implemented"));
1793217926
@@ -17935,7 +17929,7 @@ cypher_expr_atom:
1793517929
| cypher_var_name '{' map_proj_list_opt '}' %prec '{'
1793617930
{
1793717931
ereport(ERROR, errmsg("map projections are not yet implemented"));
17938-
}
17932+
}*/
1793917933
| expr_case
1794017934
| cypher_expr_func
1794117935
| '(' cypher_query ')' %prec UNARY_MINUS
@@ -18117,11 +18111,53 @@ map_keyval_list:
1811718111
;
1811818112

1811918113
list:
18120-
'[' cypher_expr_list_opt ']'
18114+
'[' cypher_stmt ']' %prec '.'
18115+
{
18116+
//cypher_pattern_comprehension *comp = make_ag_node(cypher_pattern_comprehension);
18117+
18118+
//comp->pattern = $2;
18119+
cypher_return *ret = llast($2);
18120+
if (!is_ag_node(ret, cypher_return))
18121+
ereport(ERROR, errmsg("Pattern Comprehensions must end with a return"));
18122+
18123+
if (list_length(ret->items) != 1)
18124+
ereport(ERROR, errmsg("RETURN clause for Pattern Comprehensions can only contain one item"));
18125+
18126+
ResTarget *res = linitial(ret->items);
18127+
res->val = (Node *)makeFuncCall(list_make2(makeString("postgraph"), makeString("collect")), list_make1(res->val), COERCE_SQL_SYNTAX, @1);
18128+
18129+
18130+
cypher_sub_pattern *sub;
18131+
18132+
sub = make_ag_node(cypher_sub_pattern);
18133+
sub->pattern = $2;
18134+
sub->kind = CSP_EXISTS;
18135+
18136+
SubLink *n = makeNode(SubLink);
18137+
n->subLinkType = EXPR_SUBLINK;
18138+
n->subLinkId = 0;
18139+
n->testexpr = NULL;
18140+
n->operName = NIL;
18141+
n->subselect = sub;
18142+
n->location = @1;
18143+
$$ = (Node *)n;
18144+
18145+
}
18146+
/*| '[' anonymous_path where_clause '|' return_item_list ']' %prec '.'
18147+
{
18148+
cypher_pattern_comprehension *comp = make_ag_node(cypher_pattern_comprehension);
18149+
18150+
comp->pattern = $2;
18151+
comp->where = $3;
18152+
comp->return_list = $5;
18153+
comp->location = @1;
18154+
18155+
$$ = (Node *)comp;
18156+
}*/
18157+
| '[' cypher_expr_list_opt ']' %prec '['
1812118158
{
18122-
cypher_list *n;
18159+
cypher_list *n = make_ag_node(cypher_list);
1812318160

18124-
n = make_ag_node(cypher_list);
1812518161
n->elems = $2;
1812618162

1812718163
$$ = (Node *)n;

0 commit comments

Comments
 (0)