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