Skip to content

Commit d3f90a8

Browse files
committed
Add GPML logical plan and plan -> evaluator.
1 parent f59d3d1 commit d3f90a8

File tree

4 files changed

+166
-87
lines changed

4 files changed

+166
-87
lines changed

partiql-eval/src/eval/expr/graph_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ mod tests {
103103

104104
#[track_caller]
105105
fn test_graph(matcher: PathPatternMatch<StringGraphTypes>, expected: &'static str) {
106-
let mut eval = EvalGraphMatch::new(matcher)
106+
let eval = EvalGraphMatch::new(matcher)
107107
.bind::<false>(vec![graph_reference()])
108108
.expect("graph match bind");
109109

partiql-eval/src/plan.rs

Lines changed: 136 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
1-
use itertools::{Either, Itertools};
2-
use partiql_logical as logical;
3-
use partiql_logical::{
4-
AggFunc, BagOperator, BinaryOp, BindingsOp, CallName, GraphMatchExpr, GroupingStrategy,
5-
IsTypeExpr, JoinKind, Lit, LogicalPlan, OpId, PathComponent, Pattern, PatternMatchExpr,
6-
SearchedCase, SetQuantifier, SortSpecNullOrder, SortSpecOrder, Type, UnaryOp, ValueExpr,
7-
VarRefType,
8-
};
9-
use petgraph::prelude::StableGraph;
10-
use std::collections::HashMap;
11-
121
use crate::error::{ErrorNode, PlanErr, PlanningError};
132
use crate::eval;
143
use crate::eval::evaluable::{
@@ -23,11 +12,22 @@ use crate::eval::expr::{
2312
EvalLikeNonStringNonLiteralMatch, EvalListExpr, EvalLitExpr, EvalOpBinary, EvalOpUnary,
2413
EvalPath, EvalSearchedCaseExpr, EvalStringFn, EvalTrimFn, EvalTupleExpr, EvalVarRef,
2514
};
15+
use crate::eval::graph::string_graph::StringGraphTypes;
2616
use crate::eval::EvalPlan;
17+
use itertools::{Either, Itertools};
2718
use partiql_catalog::catalog::{Catalog, FunctionEntryFunction};
2819
use partiql_extension_ion::boxed_ion::BoxedIonType;
20+
use partiql_logical as logical;
21+
use partiql_logical::{
22+
AggFunc, BagOperator, BinaryOp, BindingsOp, CallName, GraphMatchExpr, GroupingStrategy,
23+
IsTypeExpr, JoinKind, Lit, LogicalPlan, OpId, PathComponent, Pattern, PatternMatchExpr,
24+
SearchedCase, SetQuantifier, SortSpecNullOrder, SortSpecOrder, Type, UnaryOp, ValueExpr,
25+
VarRefType,
26+
};
2927
use partiql_value::boxed_variant::DynBoxedVariantTypeFactory;
3028
use partiql_value::{Bag, List, Tuple, Value, Variant};
29+
use petgraph::prelude::StableGraph;
30+
use std::collections::HashMap;
3131

3232
#[macro_export]
3333
macro_rules! correct_num_args_or_err {
@@ -494,13 +494,15 @@ impl<'c> EvaluatorPlanner<'c> {
494494

495495
("pattern expr", expr)
496496
}
497-
ValueExpr::GraphMatch(GraphMatchExpr { value, pattern }) => {
498-
//let pattern = plan_graph_match(pattern);
499-
let pattern = todo!();
500-
501-
let expr = EvalGraphMatch::new(pattern).bind::<{ STRICT }>(plan_args(&[value]));
502-
("graphmatch expr", expr)
503-
}
497+
ValueExpr::GraphMatch(GraphMatchExpr { value, pattern }) => (
498+
"graphmatch expr",
499+
match plan_graph_plan(pattern) {
500+
Ok(pattern) => {
501+
EvalGraphMatch::new(pattern).bind::<{ STRICT }>(plan_args(&[value]))
502+
}
503+
Err(e) => Ok(self.err(e) as Box<dyn EvalExpr>),
504+
},
505+
),
504506
ValueExpr::SubQueryExpr(expr) => (
505507
"subquery",
506508
Ok(Box::new(EvalSubQueryExpr::new(
@@ -791,6 +793,122 @@ impl<'c> EvaluatorPlanner<'c> {
791793
}
792794
}
793795

796+
fn plan_graph_plan(
797+
pattern: &partiql_logical::PathPatternMatch,
798+
) -> Result<eval::graph::plan::PathPatternMatch<StringGraphTypes>, PlanningError> {
799+
use eval::graph::plan as physical;
800+
use partiql_logical as logical;
801+
802+
fn plan_bind_spec(
803+
pattern: &logical::BindSpec,
804+
) -> Result<physical::BindSpec<StringGraphTypes>, PlanningError> {
805+
Ok(physical::BindSpec(pattern.0.clone()))
806+
}
807+
808+
fn plan_label_filter(
809+
pattern: &logical::LabelFilter,
810+
) -> Result<physical::LabelFilter<StringGraphTypes>, PlanningError> {
811+
Ok(match pattern {
812+
logical::LabelFilter::Always => physical::LabelFilter::Always,
813+
logical::LabelFilter::Never => physical::LabelFilter::Never,
814+
logical::LabelFilter::Named(n) => physical::LabelFilter::Named(n.clone()),
815+
})
816+
}
817+
818+
fn plan_value_filter(
819+
pattern: &logical::ValueFilter,
820+
) -> Result<physical::ValueFilter, PlanningError> {
821+
Ok(match pattern {
822+
logical::ValueFilter::Always => physical::ValueFilter::Always,
823+
})
824+
}
825+
826+
fn plan_node_filter(
827+
pattern: &logical::NodeFilter,
828+
) -> Result<physical::NodeFilter<StringGraphTypes>, PlanningError> {
829+
Ok(physical::NodeFilter {
830+
label: plan_label_filter(&pattern.label)?,
831+
filter: plan_value_filter(&pattern.filter)?,
832+
})
833+
}
834+
835+
fn plan_edge_filter(
836+
pattern: &logical::EdgeFilter,
837+
) -> Result<physical::EdgeFilter<StringGraphTypes>, PlanningError> {
838+
Ok(physical::EdgeFilter {
839+
label: plan_label_filter(&pattern.label)?,
840+
filter: plan_value_filter(&pattern.filter)?,
841+
})
842+
}
843+
844+
fn plan_step_filter(
845+
pattern: &logical::StepFilter,
846+
) -> Result<physical::StepFilter<StringGraphTypes>, PlanningError> {
847+
let dir = match pattern.dir {
848+
logical::DirectionFilter::L => physical::DirectionFilter::L,
849+
logical::DirectionFilter::R => physical::DirectionFilter::R,
850+
logical::DirectionFilter::U => physical::DirectionFilter::U,
851+
logical::DirectionFilter::LU => physical::DirectionFilter::LU,
852+
logical::DirectionFilter::UR => physical::DirectionFilter::UR,
853+
logical::DirectionFilter::LR => physical::DirectionFilter::LR,
854+
logical::DirectionFilter::LUR => physical::DirectionFilter::LUR,
855+
};
856+
Ok(physical::StepFilter {
857+
dir,
858+
triple: plan_triple_filter(&pattern.triple)?,
859+
})
860+
}
861+
862+
fn plan_triple_filter(
863+
pattern: &logical::TripleFilter,
864+
) -> Result<physical::TripleFilter<StringGraphTypes>, PlanningError> {
865+
Ok(physical::TripleFilter {
866+
lhs: plan_node_filter(&pattern.lhs)?,
867+
e: plan_edge_filter(&pattern.e)?,
868+
rhs: plan_node_filter(&pattern.rhs)?,
869+
})
870+
}
871+
872+
fn plan_node_match(
873+
pattern: &logical::NodeMatch,
874+
) -> Result<physical::NodeMatch<StringGraphTypes>, PlanningError> {
875+
Ok(physical::NodeMatch {
876+
binder: plan_bind_spec(&pattern.binder)?,
877+
spec: plan_node_filter(&pattern.spec)?,
878+
})
879+
}
880+
881+
fn plan_path_match(
882+
pattern: &logical::PathMatch,
883+
) -> Result<physical::PathMatch<StringGraphTypes>, PlanningError> {
884+
let (l, m, r) = &pattern.binders;
885+
let binders = (plan_bind_spec(l)?, plan_bind_spec(m)?, plan_bind_spec(r)?);
886+
Ok(physical::PathMatch {
887+
binders,
888+
spec: plan_step_filter(&pattern.spec)?,
889+
})
890+
}
891+
892+
fn plan_path_pattern_match(
893+
pattern: &logical::PathPatternMatch,
894+
) -> Result<physical::PathPatternMatch<StringGraphTypes>, PlanningError> {
895+
Ok(match pattern {
896+
logical::PathPatternMatch::Node(n) => {
897+
physical::PathPatternMatch::Node(plan_node_match(n)?)
898+
}
899+
logical::PathPatternMatch::Match(m) => {
900+
physical::PathPatternMatch::Match(plan_path_match(m)?)
901+
}
902+
logical::PathPatternMatch::Concat(ms) => {
903+
let ms: Result<Vec<_>, _> = ms.iter().map(plan_path_pattern_match).collect();
904+
physical::PathPatternMatch::Concat(ms?)
905+
}
906+
})
907+
}
908+
909+
plan_path_pattern_match(pattern)
910+
}
911+
794912
fn plan_lit(lit: &Lit) -> Result<Value, PlanningError> {
795913
let lit_to_val = |lit| plan_lit(lit);
796914
Ok(match lit {

partiql-logical/src/graph.rs

Lines changed: 28 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
#[cfg(feature = "serde")]
2+
use serde::{Deserialize, Serialize};
13
use std::fmt::Debug;
2-
use std::hash::Hash;
34

45
/// A plan specification for an edge's direction filtering.
5-
#[allow(dead_code)] // TODO remove once graph planning is implemented
66
#[allow(clippy::upper_case_acronyms)]
7-
#[derive(Debug, Clone, Copy)]
7+
#[derive(Debug, Clone, Eq, PartialEq)]
8+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89
pub enum DirectionFilter {
910
L, // <-
1011
U, // ~
@@ -16,11 +17,13 @@ pub enum DirectionFilter {
1617
}
1718

1819
/// A plan specification for bind names.
19-
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
20+
#[derive(Debug, Clone, Eq, PartialEq)]
21+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2022
pub struct BindSpec(pub String);
2123

2224
/// A plan specification for label filtering.
23-
#[derive(Debug, Clone, Default)]
25+
#[derive(Default, Debug, Clone, Eq, PartialEq)]
26+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2427
pub enum LabelFilter {
2528
#[default]
2629
Always,
@@ -29,126 +32,84 @@ pub enum LabelFilter {
2932
}
3033

3134
/// A plan specification for value filtering.
32-
#[derive(Debug, Clone, Copy, Default)]
35+
#[derive(Default, Debug, Clone, Eq, PartialEq)]
36+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3337
pub enum ValueFilter {
3438
#[default]
3539
Always,
3640
// TODO other variant for, e.g., `WHERE` filters
3741
}
3842

3943
/// A plan specification for node label & value filtering.
40-
#[derive(Debug, Clone)]
44+
#[derive(Debug, Clone, Eq, PartialEq)]
45+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4146
pub struct NodeFilter {
4247
pub label: LabelFilter,
4348
pub filter: ValueFilter,
4449
}
4550

4651
/// A plan specification for edge label & value filtering.
47-
#[derive(Debug, Clone)]
52+
#[derive(Debug, Clone, Eq, PartialEq)]
53+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4854
pub struct EdgeFilter {
4955
pub label: LabelFilter,
5056
pub filter: ValueFilter,
5157
}
5258

5359
/// A plan specification for triple (node, edge, node) matching.
54-
#[derive(Debug, Clone)]
60+
#[derive(Debug, Clone, Eq, PartialEq)]
61+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5562
pub struct TripleFilter {
5663
pub lhs: NodeFilter,
5764
pub e: EdgeFilter,
5865
pub rhs: NodeFilter,
5966
}
6067

6168
/// A plan specification for 'step' (triple + edge direction) matching.
62-
#[derive(Debug, Clone)]
69+
#[derive(Debug, Clone, Eq, PartialEq)]
70+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6371
pub struct StepFilter {
6472
pub dir: DirectionFilter,
6573
pub triple: TripleFilter,
6674
}
6775

6876
/// A plan specification for 'path patterns' (i.e., sequences of 'node edge node's) matching.
69-
#[allow(dead_code)] // TODO remove once graph planning is implemented
70-
#[derive(Debug, Clone)]
77+
#[derive(Debug, Clone, Eq, PartialEq)]
78+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7179
pub struct PathPatternFilter {
7280
pub head: NodeFilter,
7381
pub tail: Vec<(DirectionFilter, EdgeFilter, NodeFilter)>,
7482
}
7583

7684
/// A plan specification for node matching.
77-
#[derive(Debug, Clone)]
85+
#[derive(Debug, Clone, Eq, PartialEq)]
86+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7887
pub struct NodeMatch {
7988
pub binder: BindSpec,
8089
pub spec: NodeFilter,
8190
}
8291

8392
/// A plan specification for edge matching.
84-
#[allow(dead_code)] // TODO remove once graph planning is implemented
85-
#[derive(Debug, Clone)]
93+
#[derive(Debug, Clone, Eq, PartialEq)]
94+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8695
pub struct EdgeMatch {
8796
pub binder: BindSpec,
8897
pub spec: EdgeFilter,
8998
}
9099

91100
/// A plan specification for path (i.e., node, edge, node) matching.
92-
#[derive(Debug, Clone)]
101+
#[derive(Debug, Clone, Eq, PartialEq)]
102+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
93103
pub struct PathMatch {
94104
pub binders: (BindSpec, BindSpec, BindSpec),
95105
pub spec: StepFilter,
96106
}
97107

98108
/// A plan specification for path patterns (i.e., sequences of [`PathMatch`]s) matching.
99-
#[allow(dead_code)] // TODO remove once graph planning is implemented
100-
#[derive(Debug, Clone)]
109+
#[derive(Debug, Clone, Eq, PartialEq)]
110+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
101111
pub enum PathPatternMatch {
102112
Node(NodeMatch),
103113
Match(PathMatch),
104114
Concat(Vec<PathPatternMatch>),
105115
}
106-
107-
impl From<PathMatch> for PathPatternMatch {
108-
fn from(value: PathMatch) -> Self {
109-
Self::Match(value)
110-
}
111-
}
112-
113-
impl From<NodeMatch> for PathPatternMatch {
114-
fn from(value: NodeMatch) -> Self {
115-
Self::Node(value)
116-
}
117-
}
118-
119-
#[allow(dead_code)] // TODO remove once graph planning is implemented
120-
pub trait ElementFilterBuilder {
121-
fn any() -> Self;
122-
fn labeled(label: String) -> Self;
123-
}
124-
125-
impl ElementFilterBuilder for NodeFilter {
126-
fn any() -> Self {
127-
Self {
128-
label: LabelFilter::Always,
129-
filter: ValueFilter::Always,
130-
}
131-
}
132-
133-
fn labeled(label: String) -> Self {
134-
Self {
135-
label: LabelFilter::Named(label),
136-
filter: ValueFilter::Always,
137-
}
138-
}
139-
}
140-
141-
impl ElementFilterBuilder for EdgeFilter {
142-
fn any() -> Self {
143-
Self {
144-
label: LabelFilter::Always,
145-
filter: ValueFilter::Always,
146-
}
147-
}
148-
fn labeled(label: String) -> Self {
149-
Self {
150-
label: LabelFilter::Named(label),
151-
filter: ValueFilter::Always,
152-
}
153-
}
154-
}

partiql-logical/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ pub struct LikeNonStringNonLiteralMatch {
609609
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
610610
pub struct GraphMatchExpr {
611611
pub value: Box<ValueExpr>,
612-
pub pattern: GraphMatchPattern,
612+
pub pattern: PathPatternMatch,
613613
}
614614
#[derive(Debug, Clone, Eq, PartialEq)]
615615
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

0 commit comments

Comments
 (0)