Skip to content

Commit eb01344

Browse files
authored
Add support for GPML-style graph query parsing (#548)
* Import 'Add experimental draft support for GPML-style graph query parsing' * Add pretty-printing for GPML * Add non-reserved keywords for some GPML keywords * Add non-reserved keyword handling to function preprocessing * Error on usage of `MATCH` syntax
1 parent 24ab5bd commit eb01344

File tree

59 files changed

+2723
-36
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+2723
-36
lines changed

extension/partiql-extension-visualize/src/ast_to_dot.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ impl ToDot<ast::Expr> for AstToDot {
165165
Expr::CallAgg(c) => self.to_dot(&mut expr_subgraph, c),
166166
Expr::Query(q) => self.to_dot(&mut expr_subgraph, q),
167167
Expr::Error => todo!(),
168+
Expr::GraphMatch(_) => todo!(),
168169
}
169170
}
170171
}

partiql-ast/src/ast.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use rust_decimal::Decimal as RustDecimal;
1212

1313
use std::fmt;
14+
use std::num::NonZeroU32;
1415

1516
#[cfg(feature = "serde")]
1617
use serde::{Deserialize, Serialize};
@@ -401,6 +402,8 @@ pub enum Expr {
401402
Path(AstNode<Path>),
402403
Call(AstNode<Call>),
403404
CallAgg(AstNode<CallAgg>),
405+
/// <expr> MATCH <graph_pattern>
406+
GraphMatch(AstNode<GraphMatch>),
404407

405408
/// Query, e.g. `UNION` | `EXCEPT` | `INTERSECT` | `SELECT` and their parts.
406409
Query(AstNode<Query>),
@@ -832,6 +835,164 @@ pub enum JoinSpec {
832835
Natural,
833836
}
834837

838+
/// `<expr> MATCH <graph_pattern>`
839+
#[derive(Visit, Clone, Debug, PartialEq)]
840+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
841+
pub struct GraphMatch {
842+
pub expr: Box<Expr>,
843+
pub graph_expr: Box<AstNode<GraphMatchExpr>>,
844+
}
845+
846+
/// The direction of an edge
847+
/// | Orientation | Edge pattern | Abbreviation |
848+
/// |---------------------------+--------------+--------------|
849+
/// | Pointing left | <−[ spec ]− | <− |
850+
/// | Undirected | ~[ spec ]~ | ~ |
851+
/// | Pointing right | −[ spec ]−> | −> |
852+
/// | Left or undirected | <~[ spec ]~ | <~ |
853+
/// | Undirected or right | ~[ spec ]~> | ~> |
854+
/// | Left or right | <−[ spec ]−> | <−> |
855+
/// | Left, undirected or right | −[ spec ]− | − |
856+
///
857+
/// Fig. 5. Table of edge patterns:
858+
/// https://arxiv.org/abs/2112.06217
859+
#[derive(Clone, Debug, PartialEq, Eq)]
860+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
861+
pub enum GraphMatchDirection {
862+
Left,
863+
Undirected,
864+
Right,
865+
LeftOrUndirected,
866+
UndirectedOrRight,
867+
LeftOrRight,
868+
LeftOrUndirectedOrRight,
869+
}
870+
871+
/// A part of a graph pattern
872+
#[derive(Clone, Debug, PartialEq)]
873+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
874+
pub enum GraphMatchPatternPart {
875+
/// A single node in a graph pattern.
876+
Node(AstNode<GraphMatchNode>),
877+
878+
/// A single edge in a graph pattern.
879+
Edge(AstNode<GraphMatchEdge>),
880+
881+
/// A sub-pattern.
882+
Pattern(AstNode<GraphMatchPattern>),
883+
}
884+
885+
/// A quantifier for graph edges or patterns. (e.g., the `{2,5}` in `MATCH (x)->{2,5}(y)`)
886+
#[derive(Clone, Debug, PartialEq, Eq)]
887+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
888+
pub struct GraphMatchQuantifier {
889+
pub lower: u32,
890+
pub upper: Option<NonZeroU32>,
891+
}
892+
893+
/// A path restrictor
894+
/// | Keyword | Description
895+
/// |----------------+--------------
896+
/// | TRAIL | No repeated edges.
897+
/// | ACYCLIC | No repeated nodes.
898+
/// | SIMPLE | No repeated nodes, except that the first and last nodes may be the same.
899+
///
900+
/// Fig. 7. Table of restrictors:
901+
/// https://arxiv.org/abs/2112.06217
902+
#[derive(Clone, Debug, PartialEq, Eq)]
903+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
904+
pub enum GraphMatchRestrictor {
905+
Trail,
906+
Acyclic,
907+
Simple,
908+
}
909+
910+
/// A single node in a graph pattern.
911+
#[derive(Visit, Clone, Debug, PartialEq)]
912+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
913+
pub struct GraphMatchNode {
914+
/// an optional node pre-filter, e.g.: `WHERE c.name='Alarm'` in `MATCH (c WHERE c.name='Alarm')`
915+
pub prefilter: Option<Box<Expr>>,
916+
/// the optional element variable of the node match, e.g.: `x` in `MATCH (x)`
917+
#[visit(skip)]
918+
pub variable: Option<SymbolPrimitive>,
919+
/// the optional label(s) to match for the node, e.g.: `Entity` in `MATCH (x:Entity)`
920+
#[visit(skip)]
921+
pub label: Option<Vec<SymbolPrimitive>>,
922+
}
923+
924+
/// A single edge in a graph pattern.
925+
#[derive(Visit, Clone, Debug, PartialEq)]
926+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
927+
pub struct GraphMatchEdge {
928+
/// edge direction
929+
#[visit(skip)]
930+
pub direction: GraphMatchDirection,
931+
/// an optional quantifier for the edge match
932+
#[visit(skip)]
933+
pub quantifier: Option<AstNode<GraphMatchQuantifier>>,
934+
/// an optional edge pre-filter, e.g.: `WHERE t.capacity>100` in `MATCH −[t:hasSupply WHERE t.capacity>100]−>`
935+
pub prefilter: Option<Box<Expr>>,
936+
/// the optional element variable of the edge match, e.g.: `t` in `MATCH −[t]−>`
937+
#[visit(skip)]
938+
pub variable: Option<SymbolPrimitive>,
939+
/// the optional label(s) to match for the edge. e.g.: `Target` in `MATCH −[t:Target]−>`
940+
#[visit(skip)]
941+
pub label: Option<Vec<SymbolPrimitive>>,
942+
}
943+
944+
/// A single graph match pattern.
945+
#[derive(Visit, Clone, Debug, PartialEq)]
946+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
947+
pub struct GraphMatchPattern {
948+
#[visit(skip)]
949+
pub restrictor: Option<GraphMatchRestrictor>,
950+
/// an optional quantifier for the entire pattern match
951+
#[visit(skip)]
952+
pub quantifier: Option<AstNode<GraphMatchQuantifier>>,
953+
/// an optional pattern pre-filter, e.g.: `WHERE a.name=b.name` in `MATCH [(a)->(b) WHERE a.name=b.name]`
954+
pub prefilter: Option<Box<Expr>>,
955+
/// the optional element variable of the pattern, e.g.: `p` in `MATCH p = (a) −[t]−> (b)`
956+
#[visit(skip)]
957+
pub variable: Option<SymbolPrimitive>,
958+
/// the ordered pattern parts
959+
#[visit(skip)]
960+
pub parts: Vec<GraphMatchPatternPart>,
961+
}
962+
963+
/// A path selector
964+
/// | Keyword
965+
/// |------------------
966+
/// | ANY SHORTEST
967+
/// | ALL SHORTEST
968+
/// | ANY
969+
/// | ANY k
970+
/// | SHORTEST k
971+
/// | SHORTEST k GROUP
972+
///
973+
/// Fig. 8. Table of restrictors:
974+
/// https://arxiv.org/abs/2112.06217
975+
#[derive(Clone, Debug, PartialEq, Eq)]
976+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
977+
pub enum GraphMatchSelector {
978+
AnyShortest,
979+
AllShortest,
980+
Any,
981+
AnyK(NonZeroU32),
982+
ShortestK(NonZeroU32),
983+
ShortestKGroup(NonZeroU32),
984+
}
985+
986+
/// A graph match clause as defined in GPML
987+
/// See https://arxiv.org/abs/2112.06217
988+
#[derive(Visit, Clone, Debug, PartialEq)]
989+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
990+
pub struct GraphMatchExpr {
991+
#[visit(skip)]
992+
pub selector: Option<GraphMatchSelector>,
993+
pub patterns: Vec<AstNode<GraphMatchPattern>>,
994+
}
995+
835996
/// GROUP BY <`grouping_strategy`> <`group_key`>[, <`group_key`>]... \[AS <symbol>\]
836997
#[derive(Visit, Clone, Debug, PartialEq)]
837998
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

0 commit comments

Comments
 (0)