Skip to content

Commit 2039f20

Browse files
committed
Experimental support for MATCH as expr.
- Still doesn't allow edge as first part of a non-nested pattern - Requires parens around entire MATCH expression
1 parent f4931e5 commit 2039f20

File tree

3 files changed

+278
-246
lines changed

3 files changed

+278
-246
lines changed

partiql-ast/src/ast.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ pub enum Expr {
208208
Like(AstNode<Like>),
209209
Between(AstNode<Between>),
210210
In(AstNode<In>),
211+
/// <expr> MATCH <graph_pattern>
212+
GraphMatch(AstNode<GraphMatch>),
211213
Case(AstNode<Case>),
212214
/// Constructors
213215
Struct(AstNode<Struct>),
@@ -535,8 +537,6 @@ pub enum FromClause {
535537
FromLet(AstNode<FromLet>),
536538
/// <from_source> JOIN \[INNER | LEFT | RIGHT | FULL\] <from_source> ON <expr>
537539
Join(AstNode<Join>),
538-
/// <expr> MATCH <graph_pattern>
539-
GraphMatch(AstNode<GraphMatch>),
540540
}
541541

542542
#[derive(Clone, Debug, PartialEq)]
@@ -606,7 +606,7 @@ pub struct GraphMatch {
606606
///
607607
/// Fig. 5. Table of edge patterns:
608608
/// https://arxiv.org/abs/2112.06217
609-
#[derive(Clone, Debug, PartialEq)]
609+
#[derive(Clone, Debug, PartialEq, Eq)]
610610
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
611611
pub enum GraphMatchDirection {
612612
Left,
@@ -633,7 +633,7 @@ pub enum GraphMatchPatternPart {
633633
}
634634

635635
/// A quantifier for graph edges or patterns. (e.g., the `{2,5}` in `MATCH (x)->{2,5}(y)`)
636-
#[derive(Clone, Debug, PartialEq)]
636+
#[derive(Clone, Debug, PartialEq, Eq)]
637637
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
638638
pub struct GraphMatchQuantifier {
639639
pub lower: u32,
@@ -649,7 +649,7 @@ pub struct GraphMatchQuantifier {
649649
///
650650
/// Fig. 7. Table of restrictors:
651651
/// https://arxiv.org/abs/2112.06217
652-
#[derive(Clone, Debug, PartialEq)]
652+
#[derive(Clone, Debug, PartialEq, Eq)]
653653
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
654654
pub enum GraphMatchRestrictor {
655655
Trail,
@@ -712,7 +712,7 @@ pub struct GraphMatchPattern {
712712
///
713713
/// Fig. 8. Table of restrictors:
714714
/// https://arxiv.org/abs/2112.06217
715-
#[derive(Clone, Debug, PartialEq)]
715+
#[derive(Clone, Debug, PartialEq, Eq)]
716716
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
717717
pub enum GraphMatchSelector {
718718
AnyShortest,

partiql-parser/src/parse/mod.rs

Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -684,111 +684,122 @@ mod tests {
684684

685685
#[test]
686686
fn no_labels() {
687-
parse!(r#"SELECT 1 FROM my_graph MATCH ()"#);
688-
parse!(r#"SELECT 1 FROM my_graph MATCH () WHERE contains_value('1')"#);
689-
parse!(r#"SELECT x.info AS info FROM my_graph MATCH (x) WHERE x.name LIKE 'foo'"#);
690-
//parse!(r#"SELECT 1 FROM g MATCH -[]-> "#);
687+
parse!(r#"SELECT 1 FROM (my_graph MATCH ())"#);
688+
parse!(r#"SELECT 1 FROM (my_graph MATCH ()) WHERE contains_value('1')"#);
689+
parse!(r#"SELECT x.info AS info FROM (my_graph MATCH (x)) WHERE x.name LIKE 'foo'"#);
690+
// TODO fails due to edge first
691+
// parse!(r#"SELECT 1 FROM (g MATCH -[]->) "#);
692+
}
693+
694+
#[test]
695+
fn lone_match_expr() {
696+
parse!(r#"(MyGraph MATCH (x))"#);
697+
parse!(r#"(MyGraph MATCH (x), (y) )"#);
698+
// TODO fails due to edge first
699+
//parse!(r#"(MyGraph MATCH (x), -[u]-> )"#);
691700
}
692701

693702
#[test]
694703
fn labelled_nodes() {
695-
parse!(r#"SELECT x AS target FROM my_graph MATCH (x:Label) WHERE x.has_data = true"#);
704+
parse!(r#"SELECT x AS target FROM (my_graph MATCH (x:Label)) WHERE x.has_data = true"#);
696705
}
697706

698707
#[test]
699708
fn edges() {
700-
parse!(r#"SELECT a,b FROM g MATCH (a:A) -[e:E]-> (b:B)"#);
701-
parse!(r#"SELECT a,b FROM g MATCH (a:A) -> (b:B)"#);
702-
parse!(r#"SELECT a,b FROM g MATCH (a:A) ~[e:E]~ (b:B)"#);
703-
parse!(r#"SELECT a,b FROM g MATCH (a:A) ~ (b:B)"#);
704-
parse!(r#"SELECT a,b FROM g MATCH (a:A) <-[e:E]- (b:B)"#);
705-
parse!(r#"SELECT a,b FROM g MATCH (a:A) <- (b:B)"#);
706-
parse!(r#"SELECT a,b FROM g MATCH (a:A) ~[e:E]~> (b:B)"#);
707-
parse!(r#"SELECT a,b FROM g MATCH (a:A) ~> (b:B)"#);
708-
parse!(r#"SELECT a,b FROM g MATCH (a:A) <~[e:E]~ (b:B)"#);
709-
parse!(r#"SELECT a,b FROM g MATCH (a:A) <~ (b:B)"#);
710-
parse!(r#"SELECT a,b FROM g MATCH (a:A) <-[e:E]-> (b:B)"#);
711-
parse!(r#"SELECT a,b FROM g MATCH (a:A) <-> (b:B)"#);
712-
parse!(r#"SELECT a,b FROM g MATCH (a:A) -[e:E]- (b:B)"#);
713-
parse!(r#"SELECT a,b FROM g MATCH (a:A) - (b:B)"#);
709+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]-> (b:B))"#);
710+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) -> (b:B))"#);
711+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~[e:E]~ (b:B))"#);
712+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~ (b:B))"#);
713+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-[e:E]- (b:B))"#);
714+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <- (b:B))"#);
715+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~[e:E]~> (b:B))"#);
716+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~> (b:B))"#);
717+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <~[e:E]~ (b:B))"#);
718+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <~ (b:B))"#);
719+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-[e:E]-> (b:B))"#);
720+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-> (b:B))"#);
721+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]- (b:B))"#);
722+
parse!(r#"SELECT a,b FROM (g MATCH (a:A) - (b:B))"#);
714723
}
715724

716725
#[test]
717726
fn quantifiers() {
718-
parse!(r#"SELECT a,b FROM g MATCH (a:A)-[:edge]->*(b:B)"#);
719-
parse!(r#"SELECT a,b FROM g MATCH (a:A)<-[:edge]-+(b:B)"#);
720-
parse!(r#"SELECT a,b FROM g MATCH (a:A)~[:edge]~{5,}(b:B)"#);
721-
parse!(r#"SELECT a,b FROM g MATCH (a:A)-[e:edge]-{2,6}(b:B)"#);
722-
parse!(r#"SELECT a,b FROM g MATCH (a:A)->*(b:B)"#);
723-
parse!(r#"SELECT a,b FROM g MATCH (a:A)<-+(b:B)"#);
724-
parse!(r#"SELECT a,b FROM g MATCH (a:A)~{5,}(b:B)"#);
725-
parse!(r#"SELECT a,b FROM g MATCH (a:A)-{2,6}(b:B)"#);
727+
parse!(r#"SELECT a,b FROM (g MATCH (a:A)-[:edge]->*(b:B))"#);
728+
parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-[:edge]-+(b:B))"#);
729+
parse!(r#"SELECT a,b FROM (g MATCH (a:A)~[:edge]~{5,}(b:B))"#);
730+
parse!(r#"SELECT a,b FROM (g MATCH (a:A)-[e:edge]-{2,6}(b:B))"#);
731+
parse!(r#"SELECT a,b FROM (g MATCH (a:A)->*(b:B))"#);
732+
parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-+(b:B))"#);
733+
parse!(r#"SELECT a,b FROM (g MATCH (a:A)~{5,}(b:B))"#);
734+
parse!(r#"SELECT a,b FROM (g MATCH (a:A)-{2,6}(b:B))"#);
726735
}
727736

728737
#[test]
729738
fn patterns() {
730739
parse!(
731-
r#"SELECT the_a.name AS src, the_b.name AS dest FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE the_y.score > 10"#
740+
r#"SELECT the_a.name AS src, the_b.name AS dest FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b)) WHERE the_y.score > 10"#
732741
);
733-
parse!(r#""SELECT a,b FROM g MATCH (a)-[:has]->()-[:contains]->(b)""#);
742+
parse!(r#""SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))""#);
734743
parse!(r#"SELECT a,b FROM (g MATCH (a) -[:has]-> (x), (x)-[:contains]->(b))"#);
735744
}
736745

737746
#[test]
738747
fn path_var() {
739-
parse!(r#"SELECT a,b FROM g MATCH p = (a:A) -[e:E]-> (b:B)"#);
748+
parse!(r#"SELECT a,b FROM (g MATCH p = (a:A) -[e:E]-> (b:B))"#);
740749
}
741750

742751
#[test]
743752
fn paranthesized() {
744-
parse!(r#"SELECT a,b FROM g MATCH [(a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner]{2,5}"#);
745-
parse!(r#"SELECT a,b FROM g MATCH pathVar = (a:A)[()-[e:Edge]->()]{1,3}(b:B)"#);
753+
parse!(
754+
r#"SELECT a,b FROM (g MATCH [(a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner]{2,5})"#
755+
);
756+
parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)[()-[e:Edge]->()]{1,3}(b:B))"#);
746757

747758
// brackets
748-
parse!(r#"SELECT a,b FROM g MATCH pathVar = (a:A)[-[e:Edge]->]*(b:B)"#);
759+
parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)[-[e:Edge]->]*(b:B))"#);
749760
// parens
750-
parse!(r#"SELECT a,b FROM g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B)"#);
761+
parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B))"#);
751762
}
752763

753764
#[test]
754765
fn filters() {
755766
parse!(
756-
r#"SELECT u as banCandidate FROM g MATCH (p:Post Where p.isFlagged = true) <-[:createdPost]- (u:User WHERE u.isBanned = false AND u.karma < 20) -[:createdComment]->(c:Comment WHERE c.isFlagged = true) WHERE p.title LIKE '%considered harmful%'"#
767+
r#"SELECT u as banCandidate FROM (g MATCH (p:Post Where p.isFlagged = true) <-[:createdPost]- (u:User WHERE u.isBanned = false AND u.karma < 20) -[:createdComment]->(c:Comment WHERE c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%'"#
757768
);
758769
}
759770

760771
#[test]
761772
fn restrictors() {
762773
parse!(
763-
r#"SELECT p FROM g MATCH TRAIL p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
774+
r#"SELECT p FROM (g MATCH TRAIL p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
764775
);
765776
parse!(
766-
r#"SELECT p FROM g MATCH SIMPLE p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
777+
r#"SELECT p FROM (g MATCH SIMPLE p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
767778
);
768779
parse!(
769-
r#"SELECT p FROM g MATCH ACYCLIC p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
780+
r#"SELECT p FROM (g MATCH ACYCLIC p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
770781
);
771782
}
772783

773784
#[test]
774785
fn selectors() {
775786
parse!(
776-
r#"SELECT p FROM g MATCH ANY SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
787+
r#"SELECT p FROM (g MATCH ANY SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
777788
);
778789
parse!(
779-
r#"SELECT p FROM g MATCH ALL SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
790+
r#"SELECT p FROM (g MATCH ALL SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
780791
);
781792
parse!(
782-
r#"SELECT p FROM g MATCH ANY p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
793+
r#"SELECT p FROM (g MATCH ANY p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
783794
);
784795
parse!(
785-
r#"SELECT p FROM g MATCH ANY 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
796+
r#"SELECT p FROM (g MATCH ANY 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
786797
);
787798
parse!(
788-
r#"SELECT p FROM g MATCH SHORTEST 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
799+
r#"SELECT p FROM (g MATCH SHORTEST 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
789800
);
790801
parse!(
791-
r#"SELECT p FROM g MATCH SHORTEST 5 GROUP p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"#
802+
r#"SELECT p FROM (g MATCH SHORTEST 5 GROUP p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"#
792803
);
793804
}
794805

@@ -799,10 +810,16 @@ mod tests {
799810
);
800811
}
801812

813+
#[test]
814+
fn union() {
815+
parse!(r#"(MyGraph MATCH (x)) UNION SELECT * FROM tbl1"#);
816+
parse!(r#"SELECT * FROM tbl1 UNION (MyGraph MATCH (x))"#);
817+
}
818+
802819
#[test]
803820
fn etc() {
804-
parse!("SELECT * FROM g MATCH ALL SHORTEST [ (x)-[e]->*(y) ]");
805-
parse!("SELECT * FROM g MATCH ALL SHORTEST [ TRAIL (x)-[e]->*(y) ]");
821+
parse!("SELECT * FROM (g MATCH ALL SHORTEST [ (x)-[e]->*(y) ])");
822+
parse!("SELECT * FROM (g MATCH ALL SHORTEST [ TRAIL (x)-[e]->*(y) ])");
806823
}
807824
}
808825

0 commit comments

Comments
 (0)