1pub mod advanced_statistics;
4pub mod algebra;
5pub mod binding_optimizer;
6pub mod cost_based_optimizer;
7pub mod cost_estimator;
8pub mod distributed;
9pub mod exec;
10pub mod functions;
11pub mod gpu;
12pub mod jit;
13pub mod ml_optimizer;
14pub mod optimizer;
15pub mod parser;
16pub mod pattern_optimizer;
17pub mod pattern_unification;
18pub mod plan;
19pub mod plan_cache;
20pub mod profiled_plan_builder;
21pub mod property_function_registry;
22pub mod property_paths;
23pub mod query_plan_visualizer;
24pub mod query_profiler;
25pub mod result_cache;
26pub mod sparql_algebra;
27pub mod sparql_query;
28pub mod statistics;
29pub mod streaming_results;
30pub mod update;
31pub mod wasm;
32
33pub use property_function_registry::{
35 PropertyFunction, PropertyFunctionArg, PropertyFunctionBinding, PropertyFunctionFactory,
36 PropertyFunctionMetadata, PropertyFunctionRegistry, PropertyFunctionResult,
37};
38
39pub use crate::{GraphName, Triple};
41pub use sparql_algebra::{
42 Expression as SparqlExpression, GraphPattern as SparqlGraphPattern, NamedNodePattern,
43 PropertyPathExpression, TermPattern as SparqlTermPattern, TriplePattern as SparqlTriplePattern,
44};
45pub use sparql_query::*;
46
47pub use plan::ExecutionPlan;
49
50pub use advanced_statistics::{AdvancedStatistics, AdvancedStatisticsCollector, PatternExecution};
52
53pub use algebra::{
56 AlgebraTriplePattern, Expression as AlgebraExpression, GraphPattern as AlgebraGraphPattern,
57 PropertyPath, Query as AlgebraQuery, TermPattern as AlgebraTermPattern,
58};
59pub use binding_optimizer::{BindingIterator, BindingOptimizer, BindingSet, Constraint, TermType};
60pub use cost_based_optimizer::{
61 CostBasedOptimizer, CostConfiguration, Optimization, OptimizedPlan, OptimizerStats,
62};
63pub use distributed::{DistributedConfig, DistributedQueryEngine, FederatedEndpoint};
64pub use gpu::{GpuBackend, GpuQueryExecutor};
65pub use jit::{JitCompiler, JitConfig};
66pub use ml_optimizer::{
67 MLOptimizationResult, MLOptimizerConfig, MLQueryOptimizer, PatternFeatures, PerformanceMetrics,
68 TrainingStats,
69};
70pub use optimizer::{AIQueryOptimizer, MultiQueryOptimizer};
71pub use parser::*;
72pub use pattern_optimizer::{IndexType, OptimizedPatternPlan, PatternExecutor, PatternOptimizer};
73pub use pattern_unification::{
74 PatternConverter, PatternOptimizer as UnifiedPatternOptimizer, UnifiedTermPattern,
75 UnifiedTriplePattern,
76};
77pub use plan_cache::{
78 CacheConfig, CacheStatistics, CachedPlan, LruQueryPlanCache, PlanCacheStats, QueryPlan,
79 QueryPlanCache, SerializablePlan,
80};
81pub use profiled_plan_builder::{
82 CacheEffectiveness, ExecutionComparison, ImprovementLevel, PerformanceAnalysis,
83 PerformanceGrade, ProfiledPlanBuilder, ProfilingReport,
84};
85pub use query_plan_visualizer::{
86 HintSeverity, OptimizationHint, QueryPlanNode, QueryPlanSummary, QueryPlanVisualizer,
87};
88pub use query_profiler::{
89 ProfiledQuery, ProfilerConfig, ProfilingStatistics, QueryProfiler, QueryProfilingSession,
90 QueryStatistics,
91};
92pub use result_cache::{CacheConfig as ResultCacheConfig, CacheStats, QueryResultCache};
93pub use statistics::{
94 GraphStatistics, PredicateStatistics, QueryExecutionStats, SelectivityInfo, StatisticsSummary,
95};
96pub use streaming_results::{
97 ConstructResults, SelectResults, Solution as StreamingSolution, SolutionMetadata,
98 StreamingConfig, StreamingProgress, StreamingQueryResults, StreamingResultBuilder,
99};
100pub use update::{UpdateExecutor, UpdateParser};
101pub use wasm::{OptimizationLevel, WasmQueryCompiler, WasmTarget};
102
103pub use exec::{QueryExecutor, QueryResults, Solution};
105
106use crate::model::{Object, Predicate, Subject, Term, Variable};
107use crate::OxirsError;
108use crate::Store;
109use std::collections::HashMap;
110use std::future::Future;
111use std::pin::Pin;
112
113use algebra::TermPattern;
115
116type FederatedQueryFuture<'a> =
118 Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Term>>, OxirsError>> + 'a>>;
119
120#[derive(Debug, Clone)]
122pub enum QueryResult {
123 Select {
125 variables: Vec<String>,
126 bindings: Vec<HashMap<String, Term>>,
127 },
128 Ask(bool),
130 Construct(Vec<crate::model::Triple>),
132}
133
134pub struct QueryEngine {
136 parser: parser::SparqlParser,
138 executor_config: QueryExecutorConfig,
140 federation_executor: Option<crate::federation::FederationExecutor>,
142}
143
144impl Default for QueryEngine {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150#[derive(Debug, Clone)]
152pub struct QueryExecutorConfig {
153 pub max_results: usize,
155 pub timeout_ms: Option<u64>,
157 pub optimize: bool,
159}
160
161impl Default for QueryExecutorConfig {
162 fn default() -> Self {
163 Self {
164 max_results: 10000,
165 timeout_ms: Some(30000),
166 optimize: true,
167 }
168 }
169}
170
171impl QueryEngine {
172 pub fn new() -> Self {
174 Self {
175 parser: parser::SparqlParser::new(),
176 executor_config: QueryExecutorConfig::default(),
177 federation_executor: crate::federation::FederationExecutor::new().ok(),
178 }
179 }
180
181 pub fn with_config(config: QueryExecutorConfig) -> Self {
183 Self {
184 parser: parser::SparqlParser::new(),
185 executor_config: config,
186 federation_executor: crate::federation::FederationExecutor::new().ok(),
187 }
188 }
189
190 pub fn with_federation(mut self) -> Self {
192 self.federation_executor = crate::federation::FederationExecutor::new().ok();
193 self
194 }
195
196 pub fn without_federation(mut self) -> Self {
198 self.federation_executor = None;
199 self
200 }
201
202 pub fn query(&self, query_str: &str, store: &dyn Store) -> Result<QueryResult, OxirsError> {
204 let parsed_query = self.parser.parse(query_str)?;
206
207 self.execute_query(&parsed_query, store)
209 }
210
211 pub fn execute_query(
213 &self,
214 query: &sparql_query::Query,
215 store: &dyn Store,
216 ) -> Result<QueryResult, OxirsError> {
217 match query {
218 sparql_query::Query::Select {
219 pattern, dataset, ..
220 } => self.execute_select_query(pattern, dataset.as_ref(), store),
221 sparql_query::Query::Ask {
222 pattern, dataset, ..
223 } => self.execute_ask_query(pattern, dataset.as_ref(), store),
224 sparql_query::Query::Construct {
225 template,
226 pattern,
227 dataset,
228 ..
229 } => self.execute_construct_query(template, pattern, dataset.as_ref(), store),
230 sparql_query::Query::Describe {
231 pattern, dataset, ..
232 } => self.execute_describe_query(pattern, dataset.as_ref(), store),
233 }
234 }
235
236 fn execute_select_query(
238 &self,
239 pattern: &SparqlGraphPattern,
240 _dataset: Option<&QueryDataset>,
241 store: &dyn Store,
242 ) -> Result<QueryResult, OxirsError> {
243 let executor = QueryExecutor::new(store);
244
245 let plan = self.pattern_to_plan(pattern)?;
247
248 let solutions = executor.execute(&plan)?;
250
251 let variables = self.extract_variables(pattern);
253 let bindings: Vec<HashMap<String, Term>> = solutions
254 .into_iter()
255 .take(self.executor_config.max_results)
256 .map(|sol| {
257 let mut binding = HashMap::new();
258 for var in &variables {
259 if let Some(term) = sol.get(var) {
260 binding.insert(var.name().to_string(), term.clone());
261 }
262 }
263 binding
264 })
265 .collect();
266
267 Ok(QueryResult::Select {
268 variables: variables
269 .into_iter()
270 .map(|v| v.name().to_string())
271 .collect(),
272 bindings,
273 })
274 }
275
276 fn execute_ask_query(
278 &self,
279 pattern: &SparqlGraphPattern,
280 _dataset: Option<&QueryDataset>,
281 store: &dyn Store,
282 ) -> Result<QueryResult, OxirsError> {
283 let executor = QueryExecutor::new(store);
284
285 let plan = self.pattern_to_plan(pattern)?;
287
288 let solutions = executor.execute(&plan)?;
290
291 Ok(QueryResult::Ask(!solutions.is_empty()))
293 }
294
295 fn execute_construct_query(
297 &self,
298 template: &[SparqlTriplePattern],
299 pattern: &SparqlGraphPattern,
300 _dataset: Option<&QueryDataset>,
301 store: &dyn Store,
302 ) -> Result<QueryResult, OxirsError> {
303 let executor = QueryExecutor::new(store);
304
305 let plan = self.pattern_to_plan(pattern)?;
307
308 let solutions = executor.execute(&plan)?;
310
311 let mut triples = Vec::new();
313 for solution in solutions.into_iter().take(self.executor_config.max_results) {
314 for triple_pattern in template {
315 if let Some(triple) = self.instantiate_triple_pattern(triple_pattern, &solution)? {
316 triples.push(triple);
317 }
318 }
319 }
320
321 Ok(QueryResult::Construct(triples))
322 }
323
324 fn execute_describe_query(
326 &self,
327 pattern: &SparqlGraphPattern,
328 _dataset: Option<&QueryDataset>,
329 store: &dyn Store,
330 ) -> Result<QueryResult, OxirsError> {
331 let executor = QueryExecutor::new(store);
334
335 let plan = self.pattern_to_plan(pattern)?;
337
338 let solutions = executor.execute(&plan)?;
340
341 let mut triples = Vec::new();
343 for solution in solutions.into_iter().take(self.executor_config.max_results) {
344 for (_, term) in solution.iter() {
346 if let Ok(store_quads) =
347 store.find_quads(None, None, None, Some(&GraphName::DefaultGraph))
348 {
349 for quad in store_quads {
350 let triple = Triple::new(
351 quad.subject().clone(),
352 quad.predicate().clone(),
353 quad.object().clone(),
354 );
355 if self.triple_involves_term(&triple, term) {
356 triples.push(triple);
357 }
358 }
359 }
360 }
361 }
362
363 triples.dedup();
364 Ok(QueryResult::Construct(triples))
365 }
366
367 fn pattern_to_plan(&self, pattern: &SparqlGraphPattern) -> Result<ExecutionPlan, OxirsError> {
369 match pattern {
370 SparqlGraphPattern::Bgp { patterns } => {
371 if patterns.len() == 1 {
372 Ok(ExecutionPlan::TripleScan {
374 pattern: self.convert_sparql_triple_pattern(&patterns[0])?,
375 })
376 } else {
377 let mut plan = ExecutionPlan::TripleScan {
379 pattern: self.convert_sparql_triple_pattern(&patterns[0])?,
380 };
381
382 for triple_pattern in &patterns[1..] {
383 let right_plan = ExecutionPlan::TripleScan {
384 pattern: self.convert_sparql_triple_pattern(triple_pattern)?,
385 };
386
387 let join_vars = self.find_join_variables(&plan, &right_plan);
389
390 plan = ExecutionPlan::HashJoin {
391 left: Box::new(plan),
392 right: Box::new(right_plan),
393 join_vars,
394 };
395 }
396
397 Ok(plan)
398 }
399 }
400 SparqlGraphPattern::Join { left, right } => {
401 let left_plan = self.pattern_to_plan(left)?;
402 let right_plan = self.pattern_to_plan(right)?;
403 let join_vars = self.find_join_variables(&left_plan, &right_plan);
404
405 Ok(ExecutionPlan::HashJoin {
406 left: Box::new(left_plan),
407 right: Box::new(right_plan),
408 join_vars,
409 })
410 }
411 SparqlGraphPattern::Filter { expr, inner } => {
412 let input_plan = self.pattern_to_plan(inner)?;
413 let condition = self.convert_expression(expr.clone())?;
415 Ok(ExecutionPlan::Filter {
416 input: Box::new(input_plan),
417 condition,
418 })
419 }
420 SparqlGraphPattern::Union { left, right } => {
421 let left_plan = self.pattern_to_plan(left)?;
422 let right_plan = self.pattern_to_plan(right)?;
423
424 Ok(ExecutionPlan::Union {
425 left: Box::new(left_plan),
426 right: Box::new(right_plan),
427 })
428 }
429 SparqlGraphPattern::Project { inner, variables } => {
430 let input_plan = self.pattern_to_plan(inner)?;
431 Ok(ExecutionPlan::Project {
432 input: Box::new(input_plan),
433 vars: variables.clone(),
434 })
435 }
436 SparqlGraphPattern::Distinct { inner } => {
437 let input_plan = self.pattern_to_plan(inner)?;
438 Ok(ExecutionPlan::Distinct {
439 input: Box::new(input_plan),
440 })
441 }
442 SparqlGraphPattern::Slice {
443 inner,
444 start,
445 length,
446 } => {
447 let input_plan = self.pattern_to_plan(inner)?;
448 Ok(ExecutionPlan::Limit {
449 input: Box::new(input_plan),
450 limit: length.unwrap_or(usize::MAX),
451 offset: *start,
452 })
453 }
454 _ => {
455 Err(OxirsError::Query(format!(
457 "Unsupported graph pattern type: {pattern:?}"
458 )))
459 }
460 }
461 }
462
463 fn convert_sparql_triple_pattern(
465 &self,
466 pattern: &SparqlTriplePattern,
467 ) -> Result<crate::model::pattern::TriplePattern, OxirsError> {
468 use crate::model::pattern::*;
469
470 let subject = match &pattern.subject {
471 SparqlTermPattern::Variable(v) => Some(SubjectPattern::Variable(v.clone())),
472 SparqlTermPattern::NamedNode(n) => Some(SubjectPattern::NamedNode(n.clone())),
473 SparqlTermPattern::BlankNode(b) => Some(SubjectPattern::BlankNode(b.clone())),
474 _ => None,
475 };
476
477 let predicate = match &pattern.predicate {
478 SparqlTermPattern::Variable(v) => Some(PredicatePattern::Variable(v.clone())),
479 SparqlTermPattern::NamedNode(n) => Some(PredicatePattern::NamedNode(n.clone())),
480 _ => None,
481 };
482
483 let object = match &pattern.object {
484 SparqlTermPattern::Variable(v) => Some(ObjectPattern::Variable(v.clone())),
485 SparqlTermPattern::NamedNode(n) => Some(ObjectPattern::NamedNode(n.clone())),
486 SparqlTermPattern::BlankNode(b) => Some(ObjectPattern::BlankNode(b.clone())),
487 SparqlTermPattern::Literal(l) => Some(ObjectPattern::Literal(l.clone())),
488 #[cfg(feature = "sparql-12")]
489 SparqlTermPattern::Triple(_) => {
490 None
492 }
493 };
494
495 Ok(crate::model::pattern::TriplePattern {
496 subject,
497 predicate,
498 object,
499 })
500 }
501
502 #[allow(dead_code)]
504 fn convert_triple_pattern(
505 &self,
506 pattern: &AlgebraTriplePattern,
507 ) -> Result<crate::model::pattern::TriplePattern, OxirsError> {
508 use crate::model::pattern::*;
509
510 let subject = match &pattern.subject {
511 TermPattern::Variable(v) => Some(SubjectPattern::Variable(v.clone())),
512 TermPattern::NamedNode(n) => Some(SubjectPattern::NamedNode(n.clone())),
513 TermPattern::BlankNode(b) => Some(SubjectPattern::BlankNode(b.clone())),
514 _ => None,
515 };
516
517 let predicate = match &pattern.predicate {
518 TermPattern::Variable(v) => Some(PredicatePattern::Variable(v.clone())),
519 TermPattern::NamedNode(n) => Some(PredicatePattern::NamedNode(n.clone())),
520 _ => None,
521 };
522
523 let object = match &pattern.object {
524 TermPattern::Variable(v) => Some(ObjectPattern::Variable(v.clone())),
525 TermPattern::NamedNode(n) => Some(ObjectPattern::NamedNode(n.clone())),
526 TermPattern::BlankNode(b) => Some(ObjectPattern::BlankNode(b.clone())),
527 TermPattern::Literal(l) => Some(ObjectPattern::Literal(l.clone())),
528 TermPattern::QuotedTriple(_) => {
529 panic!("RDF-star quoted triples not yet fully supported in query module")
530 }
531 };
532
533 Ok(crate::model::pattern::TriplePattern {
534 subject,
535 predicate,
536 object,
537 })
538 }
539
540 fn find_join_variables(&self, _left: &ExecutionPlan, _right: &ExecutionPlan) -> Vec<Variable> {
542 Vec::new()
544 }
545
546 #[allow(clippy::only_used_in_recursion)]
548 fn convert_expression(
549 &self,
550 expr: sparql_algebra::Expression,
551 ) -> Result<AlgebraExpression, OxirsError> {
552 use sparql_algebra::Expression as SparqlExpr;
553 use AlgebraExpression as AlgebraExpr;
554
555 match expr {
556 SparqlExpr::NamedNode(n) => Ok(AlgebraExpr::Term(crate::model::Term::NamedNode(n))),
557 SparqlExpr::Literal(l) => Ok(AlgebraExpr::Term(crate::model::Term::Literal(l))),
558 SparqlExpr::Variable(v) => Ok(AlgebraExpr::Variable(v)),
559 SparqlExpr::Or(left, right) => {
560 let left_expr = self.convert_expression(*left)?;
561 let right_expr = self.convert_expression(*right)?;
562 Ok(AlgebraExpr::Or(Box::new(left_expr), Box::new(right_expr)))
563 }
564 SparqlExpr::And(left, right) => {
565 let left_expr = self.convert_expression(*left)?;
566 let right_expr = self.convert_expression(*right)?;
567 Ok(AlgebraExpr::And(Box::new(left_expr), Box::new(right_expr)))
568 }
569 SparqlExpr::Equal(left, right) => {
570 let left_expr = self.convert_expression(*left)?;
571 let right_expr = self.convert_expression(*right)?;
572 Ok(AlgebraExpr::Equal(
573 Box::new(left_expr),
574 Box::new(right_expr),
575 ))
576 }
577 SparqlExpr::SameTerm(left, right) => {
578 let left_expr = self.convert_expression(*left)?;
579 let right_expr = self.convert_expression(*right)?;
580 Ok(AlgebraExpr::Equal(
581 Box::new(left_expr),
582 Box::new(right_expr),
583 )) }
585 SparqlExpr::Greater(left, right) => {
586 let left_expr = self.convert_expression(*left)?;
587 let right_expr = self.convert_expression(*right)?;
588 Ok(AlgebraExpr::Greater(
589 Box::new(left_expr),
590 Box::new(right_expr),
591 ))
592 }
593 SparqlExpr::GreaterOrEqual(left, right) => {
594 let left_expr = self.convert_expression(*left)?;
595 let right_expr = self.convert_expression(*right)?;
596 Ok(AlgebraExpr::GreaterOrEqual(
597 Box::new(left_expr),
598 Box::new(right_expr),
599 ))
600 }
601 SparqlExpr::Less(left, right) => {
602 let left_expr = self.convert_expression(*left)?;
603 let right_expr = self.convert_expression(*right)?;
604 Ok(AlgebraExpr::Less(Box::new(left_expr), Box::new(right_expr)))
605 }
606 SparqlExpr::LessOrEqual(left, right) => {
607 let left_expr = self.convert_expression(*left)?;
608 let right_expr = self.convert_expression(*right)?;
609 Ok(AlgebraExpr::LessOrEqual(
610 Box::new(left_expr),
611 Box::new(right_expr),
612 ))
613 }
614 SparqlExpr::Not(inner) => {
615 let inner_expr = self.convert_expression(*inner)?;
616 Ok(AlgebraExpr::Not(Box::new(inner_expr)))
617 }
618 _ => {
619 Err(OxirsError::Query(format!(
621 "Expression type not yet supported in conversion: {expr:?}"
622 )))
623 }
624 }
625 }
626
627 fn extract_variables(&self, pattern: &SparqlGraphPattern) -> Vec<Variable> {
629 let mut variables = Vec::new();
630 self.collect_variables_from_pattern(pattern, &mut variables);
631 variables.sort_by_key(|v: &Variable| v.name().to_owned());
632 variables.dedup();
633 variables
634 }
635
636 fn collect_variables_from_pattern(
638 &self,
639 pattern: &SparqlGraphPattern,
640 variables: &mut Vec<Variable>,
641 ) {
642 match pattern {
643 SparqlGraphPattern::Bgp { patterns } => {
644 for triple_pattern in patterns {
645 self.collect_variables_from_triple_pattern(triple_pattern, variables);
646 }
647 }
648 SparqlGraphPattern::Join { left, right } => {
649 self.collect_variables_from_pattern(left, variables);
650 self.collect_variables_from_pattern(right, variables);
651 }
652 SparqlGraphPattern::Filter { inner, .. } => {
653 self.collect_variables_from_pattern(inner, variables);
654 }
655 SparqlGraphPattern::Union { left, right } => {
656 self.collect_variables_from_pattern(left, variables);
657 self.collect_variables_from_pattern(right, variables);
658 }
659 SparqlGraphPattern::Project {
660 inner,
661 variables: proj_vars,
662 } => {
663 self.collect_variables_from_pattern(inner, variables);
664 variables.extend(proj_vars.iter().cloned());
665 }
666 SparqlGraphPattern::Distinct { inner } => {
667 self.collect_variables_from_pattern(inner, variables);
668 }
669 SparqlGraphPattern::Slice { inner, .. } => {
670 self.collect_variables_from_pattern(inner, variables);
671 }
672 _ => {
673 }
675 }
676 }
677
678 fn collect_variables_from_triple_pattern(
680 &self,
681 pattern: &SparqlTriplePattern,
682 variables: &mut Vec<Variable>,
683 ) {
684 if let SparqlTermPattern::Variable(v) = &pattern.subject {
685 variables.push(v.clone());
686 }
687 if let SparqlTermPattern::Variable(v) = &pattern.predicate {
688 variables.push(v.clone());
689 }
690 if let SparqlTermPattern::Variable(v) = &pattern.object {
691 variables.push(v.clone());
692 }
693 }
694
695 fn instantiate_triple_pattern(
697 &self,
698 pattern: &SparqlTriplePattern,
699 solution: &Solution,
700 ) -> Result<Option<crate::model::Triple>, OxirsError> {
701 use crate::model::*;
702
703 let subject = match &pattern.subject {
704 SparqlTermPattern::Variable(v) => {
705 if let Some(term) = solution.get(v) {
706 match term {
707 Term::NamedNode(n) => Subject::NamedNode(n.clone()),
708 Term::BlankNode(b) => Subject::BlankNode(b.clone()),
709 _ => return Ok(None), }
711 } else {
712 return Ok(None); }
714 }
715 SparqlTermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
716 SparqlTermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
717 _ => return Ok(None), };
719
720 let predicate = match &pattern.predicate {
721 SparqlTermPattern::Variable(v) => {
722 if let Some(Term::NamedNode(n)) = solution.get(v) {
723 Predicate::NamedNode(n.clone())
724 } else {
725 return Ok(None); }
727 }
728 SparqlTermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
729 _ => return Ok(None), };
731
732 let object = match &pattern.object {
733 SparqlTermPattern::Variable(v) => {
734 if let Some(term) = solution.get(v) {
735 match term {
736 Term::NamedNode(n) => Object::NamedNode(n.clone()),
737 Term::BlankNode(b) => Object::BlankNode(b.clone()),
738 Term::Literal(l) => Object::Literal(l.clone()),
739 _ => return Ok(None), }
741 } else {
742 return Ok(None); }
744 }
745 SparqlTermPattern::NamedNode(n) => Object::NamedNode(n.clone()),
746 SparqlTermPattern::BlankNode(b) => Object::BlankNode(b.clone()),
747 SparqlTermPattern::Literal(l) => Object::Literal(l.clone()),
748 #[cfg(feature = "sparql-12")]
749 SparqlTermPattern::Triple(_) => {
750 return Ok(None);
752 }
753 };
754
755 Ok(Some(Triple::new(subject, predicate, object)))
756 }
757
758 pub async fn query_async(
760 &self,
761 query_str: &str,
762 store: &dyn Store,
763 ) -> Result<QueryResult, OxirsError> {
764 let parsed_query = self.parser.parse(query_str)?;
766
767 self.execute_query_async(&parsed_query, store).await
769 }
770
771 pub async fn execute_query_async(
773 &self,
774 query: &sparql_query::Query,
775 store: &dyn Store,
776 ) -> Result<QueryResult, OxirsError> {
777 match query {
778 sparql_query::Query::Select {
779 pattern, dataset, ..
780 } => {
781 self.execute_select_query_async(pattern, dataset.as_ref(), store)
782 .await
783 }
784 sparql_query::Query::Ask {
785 pattern, dataset, ..
786 } => {
787 self.execute_ask_query_async(pattern, dataset.as_ref(), store)
788 .await
789 }
790 sparql_query::Query::Construct {
791 template,
792 pattern,
793 dataset,
794 ..
795 } => {
796 self.execute_construct_query_async(template, pattern, dataset.as_ref(), store)
797 .await
798 }
799 sparql_query::Query::Describe {
800 pattern, dataset, ..
801 } => {
802 self.execute_describe_query_async(pattern, dataset.as_ref(), store)
803 .await
804 }
805 }
806 }
807
808 async fn execute_select_query_async(
810 &self,
811 pattern: &SparqlGraphPattern,
812 _dataset: Option<&QueryDataset>,
813 store: &dyn Store,
814 ) -> Result<QueryResult, OxirsError> {
815 if self.contains_service_clause(pattern) {
817 return self.execute_federated_select(pattern, store).await;
819 }
820
821 self.execute_select_query(pattern, _dataset, store)
823 }
824
825 async fn execute_ask_query_async(
827 &self,
828 pattern: &SparqlGraphPattern,
829 dataset: Option<&QueryDataset>,
830 store: &dyn Store,
831 ) -> Result<QueryResult, OxirsError> {
832 if self.contains_service_clause(pattern) {
833 let result = self.execute_federated_select(pattern, store).await?;
834 if let QueryResult::Select { bindings, .. } = result {
835 return Ok(QueryResult::Ask(!bindings.is_empty()));
836 }
837 }
838 self.execute_ask_query(pattern, dataset, store)
839 }
840
841 async fn execute_construct_query_async(
843 &self,
844 template: &[SparqlTriplePattern],
845 pattern: &SparqlGraphPattern,
846 dataset: Option<&QueryDataset>,
847 store: &dyn Store,
848 ) -> Result<QueryResult, OxirsError> {
849 if self.contains_service_clause(pattern) {
850 return Err(OxirsError::Federation(
851 "CONSTRUCT with SERVICE is not yet fully supported".to_string(),
852 ));
853 }
854 self.execute_construct_query(template, pattern, dataset, store)
855 }
856
857 async fn execute_describe_query_async(
859 &self,
860 pattern: &SparqlGraphPattern,
861 dataset: Option<&QueryDataset>,
862 store: &dyn Store,
863 ) -> Result<QueryResult, OxirsError> {
864 if self.contains_service_clause(pattern) {
865 return Err(OxirsError::Federation(
866 "DESCRIBE with SERVICE is not yet fully supported".to_string(),
867 ));
868 }
869 self.execute_describe_query(pattern, dataset, store)
870 }
871
872 fn contains_service_clause(&self, pattern: &SparqlGraphPattern) -> bool {
874 matches!(pattern, SparqlGraphPattern::Service { .. })
876 || Self::pattern_contains_service_recursive(pattern)
877 }
878
879 fn pattern_contains_service_recursive(pattern: &SparqlGraphPattern) -> bool {
881 match pattern {
882 SparqlGraphPattern::Service { .. } => true,
883 SparqlGraphPattern::Join { left, right }
884 | SparqlGraphPattern::Union { left, right } => {
885 Self::pattern_contains_service_recursive(left)
886 || Self::pattern_contains_service_recursive(right)
887 }
888 SparqlGraphPattern::Filter { inner, .. }
889 | SparqlGraphPattern::Distinct { inner }
890 | SparqlGraphPattern::Reduced { inner }
891 | SparqlGraphPattern::Project { inner, .. } => {
892 Self::pattern_contains_service_recursive(inner)
893 }
894 SparqlGraphPattern::LeftJoin { left, right, .. } => {
895 Self::pattern_contains_service_recursive(left)
896 || Self::pattern_contains_service_recursive(right)
897 }
898 _ => false,
899 }
900 }
901
902 async fn execute_federated_select(
904 &self,
905 pattern: &SparqlGraphPattern,
906 store: &dyn Store,
907 ) -> Result<QueryResult, OxirsError> {
908 let federation_executor = self.federation_executor.as_ref().ok_or_else(|| {
909 OxirsError::Federation("Federation executor not available".to_string())
910 })?;
911
912 let local_bindings = Vec::new();
914
915 let bindings = self
917 .execute_pattern_with_federation(pattern, local_bindings, federation_executor, store)
918 .await?;
919
920 let variables = self
922 .extract_variables(pattern)
923 .into_iter()
924 .map(|v| v.name().to_string())
925 .collect();
926
927 Ok(QueryResult::Select {
928 variables,
929 bindings,
930 })
931 }
932
933 fn execute_pattern_with_federation<'a>(
935 &'a self,
936 pattern: &'a SparqlGraphPattern,
937 current_bindings: Vec<HashMap<String, Term>>,
938 federation_executor: &'a crate::federation::FederationExecutor,
939 store: &'a dyn Store,
940 ) -> FederatedQueryFuture<'a> {
941 Box::pin(async move {
942 let current_bindings = current_bindings;
943 match pattern {
944 SparqlGraphPattern::Service {
945 name,
946 inner,
947 silent,
948 } => {
949 let remote_bindings = federation_executor
951 .execute_service(name, inner, *silent, ¤t_bindings)
952 .await?;
953
954 if current_bindings.is_empty() {
956 Ok(remote_bindings)
957 } else {
958 Ok(federation_executor.merge_bindings(current_bindings, remote_bindings))
959 }
960 }
961 SparqlGraphPattern::Join { left, right } => {
962 let left_bindings = self
964 .execute_pattern_with_federation(
965 left,
966 current_bindings,
967 federation_executor,
968 store,
969 )
970 .await?;
971
972 self.execute_pattern_with_federation(
974 right,
975 left_bindings,
976 federation_executor,
977 store,
978 )
979 .await
980 }
981 _ => {
982 let executor = QueryExecutor::new(store);
984 let plan = self.pattern_to_plan(pattern)?;
985 let solutions = executor.execute(&plan)?;
986
987 let bindings: Vec<HashMap<String, Term>> = solutions
988 .into_iter()
989 .take(self.executor_config.max_results)
990 .map(|sol| {
991 sol.iter()
992 .map(|(var, term)| (var.name().to_string(), term.clone()))
993 .collect()
994 })
995 .collect();
996
997 if current_bindings.is_empty() {
998 Ok(bindings)
999 } else {
1000 Ok(federation_executor.merge_bindings(current_bindings, bindings))
1002 }
1003 }
1004 }
1005 })
1006 }
1007
1008 fn triple_involves_term(&self, triple: &crate::model::Triple, term: &Term) -> bool {
1010 match term {
1011 Term::NamedNode(n) => {
1012 matches!(triple.subject(), Subject::NamedNode(sn) if sn == n)
1013 || matches!(triple.predicate(), Predicate::NamedNode(pn) if pn == n)
1014 || matches!(triple.object(), Object::NamedNode(on) if on == n)
1015 }
1016 Term::BlankNode(b) => {
1017 matches!(triple.subject(), Subject::BlankNode(sb) if sb == b)
1018 || matches!(triple.object(), Object::BlankNode(ob) if ob == b)
1019 }
1020 Term::Literal(l) => {
1021 matches!(triple.object(), Object::Literal(ol) if ol == l)
1022 }
1023 _ => false,
1024 }
1025 }
1026}