1use crate::model::pattern::{ObjectPattern, PredicatePattern, SubjectPattern, TriplePattern};
7use crate::model::*;
8use crate::query::algebra::{
9 AlgebraTriplePattern, Expression, GraphPattern, OrderExpression, Query, QueryForm,
10 SelectVariables, TermPattern,
11};
12use crate::OxirsError;
13
14pub fn convert_triple_pattern(pattern: &AlgebraTriplePattern) -> TriplePattern {
17 convert_algebra_triple_pattern(pattern)
19}
20
21pub fn convert_algebra_triple_pattern(pattern: &AlgebraTriplePattern) -> TriplePattern {
24 let subject = match &pattern.subject {
25 TermPattern::NamedNode(nn) => Some(SubjectPattern::NamedNode(nn.clone())),
26 TermPattern::BlankNode(bn) => Some(SubjectPattern::BlankNode(bn.clone())),
27 TermPattern::Variable(v) => Some(SubjectPattern::Variable(v.clone())),
28 TermPattern::Literal(_) => None, TermPattern::QuotedTriple(_) => None, };
31
32 let predicate = match &pattern.predicate {
33 TermPattern::NamedNode(nn) => Some(PredicatePattern::NamedNode(nn.clone())),
34 TermPattern::Variable(v) => Some(PredicatePattern::Variable(v.clone())),
35 _ => None, };
37
38 let object = match &pattern.object {
39 TermPattern::NamedNode(nn) => Some(ObjectPattern::NamedNode(nn.clone())),
40 TermPattern::BlankNode(bn) => Some(ObjectPattern::BlankNode(bn.clone())),
41 TermPattern::Literal(lit) => Some(ObjectPattern::Literal(lit.clone())),
42 TermPattern::Variable(v) => Some(ObjectPattern::Variable(v.clone())),
43 TermPattern::QuotedTriple(_) => None, };
45
46 TriplePattern::new(subject, predicate, object)
47}
48
49pub fn convert_to_algebra_pattern(
52 pattern: &TriplePattern,
53) -> Result<AlgebraTriplePattern, OxirsError> {
54 let subject = match pattern.subject() {
55 Some(SubjectPattern::NamedNode(nn)) => TermPattern::NamedNode(nn.clone()),
56 Some(SubjectPattern::BlankNode(bn)) => TermPattern::BlankNode(bn.clone()),
57 Some(SubjectPattern::Variable(v)) => TermPattern::Variable(v.clone()),
58 None => {
59 return Err(OxirsError::Query(
60 "Subject pattern is required in algebra representation".to_string(),
61 ))
62 }
63 };
64
65 let predicate = match pattern.predicate() {
66 Some(PredicatePattern::NamedNode(nn)) => TermPattern::NamedNode(nn.clone()),
67 Some(PredicatePattern::Variable(v)) => TermPattern::Variable(v.clone()),
68 None => {
69 return Err(OxirsError::Query(
70 "Predicate pattern is required in algebra representation".to_string(),
71 ))
72 }
73 };
74
75 let object = match pattern.object() {
76 Some(ObjectPattern::NamedNode(nn)) => TermPattern::NamedNode(nn.clone()),
77 Some(ObjectPattern::BlankNode(bn)) => TermPattern::BlankNode(bn.clone()),
78 Some(ObjectPattern::Literal(lit)) => TermPattern::Literal(lit.clone()),
79 Some(ObjectPattern::Variable(v)) => TermPattern::Variable(v.clone()),
80 None => {
81 return Err(OxirsError::Query(
82 "Object pattern is required in algebra representation".to_string(),
83 ))
84 }
85 };
86
87 Ok(AlgebraTriplePattern::new(subject, predicate, object))
88}
89
90#[derive(Debug, Clone)]
92pub enum ExecutionPlan {
93 TripleScan {
95 pattern: crate::model::pattern::TriplePattern,
96 },
97 HashJoin {
99 left: Box<ExecutionPlan>,
100 right: Box<ExecutionPlan>,
101 join_vars: Vec<Variable>,
102 },
103 Filter {
105 input: Box<ExecutionPlan>,
106 condition: Expression,
107 },
108 Project {
110 input: Box<ExecutionPlan>,
111 vars: Vec<Variable>,
112 },
113 Sort {
115 input: Box<ExecutionPlan>,
116 order_by: Vec<OrderExpression>,
117 },
118 Limit {
120 input: Box<ExecutionPlan>,
121 limit: usize,
122 offset: usize,
123 },
124 Union {
126 left: Box<ExecutionPlan>,
127 right: Box<ExecutionPlan>,
128 },
129 Distinct { input: Box<ExecutionPlan> },
131}
132
133pub struct QueryPlanner;
135
136impl QueryPlanner {
137 pub fn new() -> Self {
139 QueryPlanner
140 }
141
142 pub fn plan_query(&self, query: &Query) -> Result<ExecutionPlan, OxirsError> {
144 match &query.form {
145 QueryForm::Select {
146 where_clause,
147 variables,
148 distinct,
149 order_by,
150 limit,
151 offset,
152 ..
153 } => {
154 let mut plan = self.plan_graph_pattern(where_clause)?;
155
156 if let SelectVariables::Specific(vars) = variables {
158 plan = ExecutionPlan::Project {
159 input: Box::new(plan),
160 vars: vars.clone(),
161 };
162 }
163
164 if *distinct {
166 plan = ExecutionPlan::Distinct {
167 input: Box::new(plan),
168 };
169 }
170
171 if !order_by.is_empty() {
173 plan = ExecutionPlan::Sort {
174 input: Box::new(plan),
175 order_by: order_by.clone(),
176 };
177 }
178
179 if let Some(limit_val) = limit {
181 plan = ExecutionPlan::Limit {
182 input: Box::new(plan),
183 limit: *limit_val,
184 offset: *offset,
185 };
186 } else if *offset > 0 {
187 plan = ExecutionPlan::Limit {
188 input: Box::new(plan),
189 limit: usize::MAX,
190 offset: *offset,
191 };
192 }
193
194 Ok(plan)
195 }
196 _ => Err(OxirsError::Query(
197 "Only SELECT queries are currently supported".to_string(),
198 )),
199 }
200 }
201
202 fn plan_graph_pattern(&self, pattern: &GraphPattern) -> Result<ExecutionPlan, OxirsError> {
204 match pattern {
205 GraphPattern::Bgp(patterns) => {
206 if patterns.is_empty() {
207 return Err(OxirsError::Query("Empty basic graph pattern".to_string()));
208 }
209
210 let mut plan = ExecutionPlan::TripleScan {
212 pattern: convert_triple_pattern(&patterns[0]),
213 };
214
215 for pattern in &patterns[1..] {
217 let right_plan = ExecutionPlan::TripleScan {
218 pattern: convert_triple_pattern(pattern),
219 };
220
221 let join_vars = self.find_join_variables(&plan, &right_plan);
223
224 plan = ExecutionPlan::HashJoin {
225 left: Box::new(plan),
226 right: Box::new(right_plan),
227 join_vars,
228 };
229 }
230
231 Ok(plan)
232 }
233 GraphPattern::Filter { expr, inner } => {
234 let inner_plan = self.plan_graph_pattern(inner)?;
235 Ok(ExecutionPlan::Filter {
236 input: Box::new(inner_plan),
237 condition: expr.clone(),
238 })
239 }
240 GraphPattern::Union(left, right) => {
241 let left_plan = self.plan_graph_pattern(left)?;
242 let right_plan = self.plan_graph_pattern(right)?;
243 Ok(ExecutionPlan::Union {
244 left: Box::new(left_plan),
245 right: Box::new(right_plan),
246 })
247 }
248 _ => Err(OxirsError::Query(
249 "Graph pattern not yet supported".to_string(),
250 )),
251 }
252 }
253
254 fn find_join_variables(&self, _left: &ExecutionPlan, _right: &ExecutionPlan) -> Vec<Variable> {
256 Vec::new()
258 }
259}
260
261impl Default for QueryPlanner {
262 fn default() -> Self {
263 Self::new()
264 }
265}