use crate::model::pattern::{ObjectPattern, PredicatePattern, SubjectPattern, TriplePattern};
use crate::model::*;
use crate::query::algebra::{
AlgebraTriplePattern, Expression, GraphPattern, OrderExpression, Query, QueryForm,
SelectVariables, TermPattern,
};
use crate::OxirsError;
pub fn convert_triple_pattern(pattern: &AlgebraTriplePattern) -> TriplePattern {
convert_algebra_triple_pattern(pattern)
}
pub fn convert_algebra_triple_pattern(pattern: &AlgebraTriplePattern) -> TriplePattern {
let subject = match &pattern.subject {
TermPattern::NamedNode(nn) => Some(SubjectPattern::NamedNode(nn.clone())),
TermPattern::BlankNode(bn) => Some(SubjectPattern::BlankNode(bn.clone())),
TermPattern::Variable(v) => Some(SubjectPattern::Variable(v.clone())),
TermPattern::Literal(_) => None, TermPattern::QuotedTriple(_) => None, };
let predicate = match &pattern.predicate {
TermPattern::NamedNode(nn) => Some(PredicatePattern::NamedNode(nn.clone())),
TermPattern::Variable(v) => Some(PredicatePattern::Variable(v.clone())),
_ => None, };
let object = match &pattern.object {
TermPattern::NamedNode(nn) => Some(ObjectPattern::NamedNode(nn.clone())),
TermPattern::BlankNode(bn) => Some(ObjectPattern::BlankNode(bn.clone())),
TermPattern::Literal(lit) => Some(ObjectPattern::Literal(lit.clone())),
TermPattern::Variable(v) => Some(ObjectPattern::Variable(v.clone())),
TermPattern::QuotedTriple(_) => None, };
TriplePattern::new(subject, predicate, object)
}
pub fn convert_to_algebra_pattern(
pattern: &TriplePattern,
) -> Result<AlgebraTriplePattern, OxirsError> {
let subject = match pattern.subject() {
Some(SubjectPattern::NamedNode(nn)) => TermPattern::NamedNode(nn.clone()),
Some(SubjectPattern::BlankNode(bn)) => TermPattern::BlankNode(bn.clone()),
Some(SubjectPattern::Variable(v)) => TermPattern::Variable(v.clone()),
None => {
return Err(OxirsError::Query(
"Subject pattern is required in algebra representation".to_string(),
))
}
};
let predicate = match pattern.predicate() {
Some(PredicatePattern::NamedNode(nn)) => TermPattern::NamedNode(nn.clone()),
Some(PredicatePattern::Variable(v)) => TermPattern::Variable(v.clone()),
None => {
return Err(OxirsError::Query(
"Predicate pattern is required in algebra representation".to_string(),
))
}
};
let object = match pattern.object() {
Some(ObjectPattern::NamedNode(nn)) => TermPattern::NamedNode(nn.clone()),
Some(ObjectPattern::BlankNode(bn)) => TermPattern::BlankNode(bn.clone()),
Some(ObjectPattern::Literal(lit)) => TermPattern::Literal(lit.clone()),
Some(ObjectPattern::Variable(v)) => TermPattern::Variable(v.clone()),
None => {
return Err(OxirsError::Query(
"Object pattern is required in algebra representation".to_string(),
))
}
};
Ok(AlgebraTriplePattern::new(subject, predicate, object))
}
#[derive(Debug, Clone)]
pub enum ExecutionPlan {
TripleScan {
pattern: crate::model::pattern::TriplePattern,
},
HashJoin {
left: Box<ExecutionPlan>,
right: Box<ExecutionPlan>,
join_vars: Vec<Variable>,
},
Filter {
input: Box<ExecutionPlan>,
condition: Expression,
},
Project {
input: Box<ExecutionPlan>,
vars: Vec<Variable>,
},
Sort {
input: Box<ExecutionPlan>,
order_by: Vec<OrderExpression>,
},
Limit {
input: Box<ExecutionPlan>,
limit: usize,
offset: usize,
},
Union {
left: Box<ExecutionPlan>,
right: Box<ExecutionPlan>,
},
Distinct { input: Box<ExecutionPlan> },
}
pub struct QueryPlanner;
impl QueryPlanner {
pub fn new() -> Self {
QueryPlanner
}
pub fn plan_query(&self, query: &Query) -> Result<ExecutionPlan, OxirsError> {
match &query.form {
QueryForm::Select {
where_clause,
variables,
distinct,
order_by,
limit,
offset,
..
} => {
let mut plan = self.plan_graph_pattern(where_clause)?;
if let SelectVariables::Specific(vars) = variables {
plan = ExecutionPlan::Project {
input: Box::new(plan),
vars: vars.clone(),
};
}
if *distinct {
plan = ExecutionPlan::Distinct {
input: Box::new(plan),
};
}
if !order_by.is_empty() {
plan = ExecutionPlan::Sort {
input: Box::new(plan),
order_by: order_by.clone(),
};
}
if let Some(limit_val) = limit {
plan = ExecutionPlan::Limit {
input: Box::new(plan),
limit: *limit_val,
offset: *offset,
};
} else if *offset > 0 {
plan = ExecutionPlan::Limit {
input: Box::new(plan),
limit: usize::MAX,
offset: *offset,
};
}
Ok(plan)
}
_ => Err(OxirsError::Query(
"Only SELECT queries are currently supported".to_string(),
)),
}
}
fn plan_graph_pattern(&self, pattern: &GraphPattern) -> Result<ExecutionPlan, OxirsError> {
match pattern {
GraphPattern::Bgp(patterns) => {
if patterns.is_empty() {
return Err(OxirsError::Query("Empty basic graph pattern".to_string()));
}
let mut plan = ExecutionPlan::TripleScan {
pattern: convert_triple_pattern(&patterns[0]),
};
for pattern in &patterns[1..] {
let right_plan = ExecutionPlan::TripleScan {
pattern: convert_triple_pattern(pattern),
};
let join_vars = self.find_join_variables(&plan, &right_plan);
plan = ExecutionPlan::HashJoin {
left: Box::new(plan),
right: Box::new(right_plan),
join_vars,
};
}
Ok(plan)
}
GraphPattern::Filter { expr, inner } => {
let inner_plan = self.plan_graph_pattern(inner)?;
Ok(ExecutionPlan::Filter {
input: Box::new(inner_plan),
condition: expr.clone(),
})
}
GraphPattern::Union(left, right) => {
let left_plan = self.plan_graph_pattern(left)?;
let right_plan = self.plan_graph_pattern(right)?;
Ok(ExecutionPlan::Union {
left: Box::new(left_plan),
right: Box::new(right_plan),
})
}
_ => Err(OxirsError::Query(
"Graph pattern not yet supported".to_string(),
)),
}
}
fn find_join_variables(&self, _left: &ExecutionPlan, _right: &ExecutionPlan) -> Vec<Variable> {
Vec::new()
}
}
impl Default for QueryPlanner {
fn default() -> Self {
Self::new()
}
}