oxirs_core/query/
mod.rs

1//! SPARQL query processing module
2
3pub mod algebra;
4pub mod binding_optimizer;
5pub mod distributed;
6pub mod exec;
7pub mod functions;
8pub mod gpu;
9pub mod jit;
10pub mod optimizer;
11pub mod parser;
12pub mod pattern_optimizer;
13pub mod pattern_unification;
14pub mod plan;
15pub mod property_paths;
16pub mod sparql_algebra;
17pub mod sparql_query;
18pub mod streaming_results;
19pub mod update;
20pub mod wasm;
21
22// Re-export the enhanced SPARQL algebra and query types from sparql_algebra
23pub use crate::{GraphName, Triple};
24pub use sparql_algebra::{
25    Expression as SparqlExpression, GraphPattern as SparqlGraphPattern, NamedNodePattern,
26    PropertyPathExpression, TermPattern as SparqlTermPattern, TriplePattern as SparqlTriplePattern,
27};
28pub use sparql_query::*;
29
30// Re-export execution plan types
31pub use plan::ExecutionPlan;
32
33// Re-export algebra types (without Query to avoid conflict)
34// Use explicit aliases to avoid conflicts
35pub use algebra::{
36    AlgebraTriplePattern, Expression as AlgebraExpression, GraphPattern as AlgebraGraphPattern,
37    PropertyPath, Query as AlgebraQuery, TermPattern as AlgebraTermPattern,
38};
39pub use binding_optimizer::{BindingIterator, BindingOptimizer, BindingSet, Constraint, TermType};
40pub use distributed::{DistributedConfig, DistributedQueryEngine, FederatedEndpoint};
41pub use gpu::{GpuBackend, GpuQueryExecutor};
42pub use jit::{JitCompiler, JitConfig};
43pub use optimizer::{AIQueryOptimizer, MultiQueryOptimizer};
44pub use parser::*;
45pub use pattern_optimizer::{IndexType, OptimizedPatternPlan, PatternExecutor, PatternOptimizer};
46pub use pattern_unification::{
47    PatternConverter, PatternOptimizer as UnifiedPatternOptimizer, UnifiedTermPattern,
48    UnifiedTriplePattern,
49};
50pub use streaming_results::{
51    ConstructResults, SelectResults, Solution as StreamingSolution, SolutionMetadata,
52    StreamingConfig, StreamingProgress, StreamingQueryResults, StreamingResultBuilder,
53};
54pub use update::{UpdateExecutor, UpdateParser};
55pub use wasm::{OptimizationLevel, WasmQueryCompiler, WasmTarget};
56
57// TODO: Temporary compatibility layer for SHACL module
58pub use exec::{QueryExecutor, QueryResults, Solution};
59
60use crate::model::{Object, Predicate, Subject, Term, Variable};
61use crate::OxirsError;
62use crate::Store;
63use std::collections::HashMap;
64use std::future::Future;
65use std::pin::Pin;
66
67// Import TermPattern for internal usage
68use algebra::TermPattern;
69
70/// Simplified QueryResult for SHACL compatibility
71#[derive(Debug, Clone)]
72pub enum QueryResult {
73    /// SELECT query results
74    Select {
75        variables: Vec<String>,
76        bindings: Vec<HashMap<String, Term>>,
77    },
78    /// ASK query results
79    Ask(bool),
80    /// CONSTRUCT query results
81    Construct(Vec<crate::model::Triple>),
82}
83
84/// Simplified QueryEngine for SHACL compatibility
85pub struct QueryEngine {
86    /// Query parser for converting SPARQL strings to Query objects
87    parser: parser::SparqlParser,
88    /// Query executor for executing plans
89    executor_config: QueryExecutorConfig,
90    /// Federation executor for SERVICE clause support
91    federation_executor: Option<crate::federation::FederationExecutor>,
92}
93
94impl Default for QueryEngine {
95    fn default() -> Self {
96        Self::new()
97    }
98}
99
100/// Configuration for query execution
101#[derive(Debug, Clone)]
102pub struct QueryExecutorConfig {
103    /// Maximum number of results to return
104    pub max_results: usize,
105    /// Query timeout in milliseconds
106    pub timeout_ms: Option<u64>,
107    /// Enable query optimization
108    pub optimize: bool,
109}
110
111impl Default for QueryExecutorConfig {
112    fn default() -> Self {
113        Self {
114            max_results: 10000,
115            timeout_ms: Some(30000),
116            optimize: true,
117        }
118    }
119}
120
121impl QueryEngine {
122    /// Create a new query engine
123    pub fn new() -> Self {
124        Self {
125            parser: parser::SparqlParser::new(),
126            executor_config: QueryExecutorConfig::default(),
127            federation_executor: crate::federation::FederationExecutor::new().ok(),
128        }
129    }
130
131    /// Create a new query engine with custom configuration
132    pub fn with_config(config: QueryExecutorConfig) -> Self {
133        Self {
134            parser: parser::SparqlParser::new(),
135            executor_config: config,
136            federation_executor: crate::federation::FederationExecutor::new().ok(),
137        }
138    }
139
140    /// Enable federation support
141    pub fn with_federation(mut self) -> Self {
142        self.federation_executor = crate::federation::FederationExecutor::new().ok();
143        self
144    }
145
146    /// Disable federation support
147    pub fn without_federation(mut self) -> Self {
148        self.federation_executor = None;
149        self
150    }
151
152    /// Execute a SPARQL query string against a store
153    pub fn query(&self, query_str: &str, store: &dyn Store) -> Result<QueryResult, OxirsError> {
154        // Parse the query string
155        let parsed_query = self.parser.parse(query_str)?;
156
157        // Execute the parsed query
158        self.execute_query(&parsed_query, store)
159    }
160
161    /// Execute a parsed Query object against a store
162    pub fn execute_query(
163        &self,
164        query: &sparql_query::Query,
165        store: &dyn Store,
166    ) -> Result<QueryResult, OxirsError> {
167        match query {
168            sparql_query::Query::Select {
169                pattern, dataset, ..
170            } => self.execute_select_query(pattern, dataset.as_ref(), store),
171            sparql_query::Query::Ask {
172                pattern, dataset, ..
173            } => self.execute_ask_query(pattern, dataset.as_ref(), store),
174            sparql_query::Query::Construct {
175                template,
176                pattern,
177                dataset,
178                ..
179            } => self.execute_construct_query(template, pattern, dataset.as_ref(), store),
180            sparql_query::Query::Describe {
181                pattern, dataset, ..
182            } => self.execute_describe_query(pattern, dataset.as_ref(), store),
183        }
184    }
185
186    /// Execute a SELECT query
187    fn execute_select_query(
188        &self,
189        pattern: &SparqlGraphPattern,
190        _dataset: Option<&QueryDataset>,
191        store: &dyn Store,
192    ) -> Result<QueryResult, OxirsError> {
193        let executor = QueryExecutor::new(store);
194
195        // Convert graph pattern to execution plan
196        let plan = self.pattern_to_plan(pattern)?;
197
198        // Execute the plan
199        let solutions = executor.execute(&plan)?;
200
201        // Extract variable names and convert solutions
202        let variables = self.extract_variables(pattern);
203        let bindings: Vec<HashMap<String, Term>> = solutions
204            .into_iter()
205            .take(self.executor_config.max_results)
206            .map(|sol| {
207                let mut binding = HashMap::new();
208                for var in &variables {
209                    if let Some(term) = sol.get(var) {
210                        binding.insert(var.name().to_string(), term.clone());
211                    }
212                }
213                binding
214            })
215            .collect();
216
217        Ok(QueryResult::Select {
218            variables: variables
219                .into_iter()
220                .map(|v| v.name().to_string())
221                .collect(),
222            bindings,
223        })
224    }
225
226    /// Execute an ASK query
227    fn execute_ask_query(
228        &self,
229        pattern: &SparqlGraphPattern,
230        _dataset: Option<&QueryDataset>,
231        store: &dyn Store,
232    ) -> Result<QueryResult, OxirsError> {
233        let executor = QueryExecutor::new(store);
234
235        // Convert graph pattern to execution plan
236        let plan = self.pattern_to_plan(pattern)?;
237
238        // Execute the plan
239        let solutions = executor.execute(&plan)?;
240
241        // ASK query returns true if there are any solutions
242        Ok(QueryResult::Ask(!solutions.is_empty()))
243    }
244
245    /// Execute a CONSTRUCT query
246    fn execute_construct_query(
247        &self,
248        template: &[SparqlTriplePattern],
249        pattern: &SparqlGraphPattern,
250        _dataset: Option<&QueryDataset>,
251        store: &dyn Store,
252    ) -> Result<QueryResult, OxirsError> {
253        let executor = QueryExecutor::new(store);
254
255        // Convert graph pattern to execution plan
256        let plan = self.pattern_to_plan(pattern)?;
257
258        // Execute the plan
259        let solutions = executor.execute(&plan)?;
260
261        // Construct triples from template and solutions
262        let mut triples = Vec::new();
263        for solution in solutions.into_iter().take(self.executor_config.max_results) {
264            for triple_pattern in template {
265                if let Some(triple) = self.instantiate_triple_pattern(triple_pattern, &solution)? {
266                    triples.push(triple);
267                }
268            }
269        }
270
271        Ok(QueryResult::Construct(triples))
272    }
273
274    /// Execute a DESCRIBE query
275    fn execute_describe_query(
276        &self,
277        pattern: &SparqlGraphPattern,
278        _dataset: Option<&QueryDataset>,
279        store: &dyn Store,
280    ) -> Result<QueryResult, OxirsError> {
281        // For now, treat DESCRIBE like CONSTRUCT *
282        // This is a simplified implementation
283        let executor = QueryExecutor::new(store);
284
285        // Convert graph pattern to execution plan
286        let plan = self.pattern_to_plan(pattern)?;
287
288        // Execute the plan
289        let solutions = executor.execute(&plan)?;
290
291        // Get all triples involving the found entities
292        let mut triples = Vec::new();
293        for solution in solutions.into_iter().take(self.executor_config.max_results) {
294            // For each bound entity, get all triples where it appears
295            for (_, term) in solution.iter() {
296                if let Ok(store_quads) =
297                    store.find_quads(None, None, None, Some(&GraphName::DefaultGraph))
298                {
299                    for quad in store_quads {
300                        let triple = Triple::new(
301                            quad.subject().clone(),
302                            quad.predicate().clone(),
303                            quad.object().clone(),
304                        );
305                        if self.triple_involves_term(&triple, term) {
306                            triples.push(triple);
307                        }
308                    }
309                }
310            }
311        }
312
313        triples.dedup();
314        Ok(QueryResult::Construct(triples))
315    }
316
317    /// Convert a graph pattern to an execution plan
318    fn pattern_to_plan(&self, pattern: &SparqlGraphPattern) -> Result<ExecutionPlan, OxirsError> {
319        match pattern {
320            SparqlGraphPattern::Bgp { patterns } => {
321                if patterns.len() == 1 {
322                    // Single triple pattern
323                    Ok(ExecutionPlan::TripleScan {
324                        pattern: self.convert_sparql_triple_pattern(&patterns[0])?,
325                    })
326                } else {
327                    // Multiple patterns - join them
328                    let mut plan = ExecutionPlan::TripleScan {
329                        pattern: self.convert_sparql_triple_pattern(&patterns[0])?,
330                    };
331
332                    for triple_pattern in &patterns[1..] {
333                        let right_plan = ExecutionPlan::TripleScan {
334                            pattern: self.convert_sparql_triple_pattern(triple_pattern)?,
335                        };
336
337                        // Find join variables
338                        let join_vars = self.find_join_variables(&plan, &right_plan);
339
340                        plan = ExecutionPlan::HashJoin {
341                            left: Box::new(plan),
342                            right: Box::new(right_plan),
343                            join_vars,
344                        };
345                    }
346
347                    Ok(plan)
348                }
349            }
350            SparqlGraphPattern::Join { left, right } => {
351                let left_plan = self.pattern_to_plan(left)?;
352                let right_plan = self.pattern_to_plan(right)?;
353                let join_vars = self.find_join_variables(&left_plan, &right_plan);
354
355                Ok(ExecutionPlan::HashJoin {
356                    left: Box::new(left_plan),
357                    right: Box::new(right_plan),
358                    join_vars,
359                })
360            }
361            SparqlGraphPattern::Filter { expr, inner } => {
362                let input_plan = self.pattern_to_plan(inner)?;
363                // Convert sparql_algebra::Expression to algebra::Expression
364                let condition = self.convert_expression(expr.clone())?;
365                Ok(ExecutionPlan::Filter {
366                    input: Box::new(input_plan),
367                    condition,
368                })
369            }
370            SparqlGraphPattern::Union { left, right } => {
371                let left_plan = self.pattern_to_plan(left)?;
372                let right_plan = self.pattern_to_plan(right)?;
373
374                Ok(ExecutionPlan::Union {
375                    left: Box::new(left_plan),
376                    right: Box::new(right_plan),
377                })
378            }
379            SparqlGraphPattern::Project { inner, variables } => {
380                let input_plan = self.pattern_to_plan(inner)?;
381                Ok(ExecutionPlan::Project {
382                    input: Box::new(input_plan),
383                    vars: variables.clone(),
384                })
385            }
386            SparqlGraphPattern::Distinct { inner } => {
387                let input_plan = self.pattern_to_plan(inner)?;
388                Ok(ExecutionPlan::Distinct {
389                    input: Box::new(input_plan),
390                })
391            }
392            SparqlGraphPattern::Slice {
393                inner,
394                start,
395                length,
396            } => {
397                let input_plan = self.pattern_to_plan(inner)?;
398                Ok(ExecutionPlan::Limit {
399                    input: Box::new(input_plan),
400                    limit: length.unwrap_or(usize::MAX),
401                    offset: *start,
402                })
403            }
404            _ => {
405                // For unsupported patterns, return an error for now
406                Err(OxirsError::Query(format!(
407                    "Unsupported graph pattern type: {pattern:?}"
408                )))
409            }
410        }
411    }
412
413    /// Convert a SPARQL triple pattern to a model triple pattern
414    fn convert_sparql_triple_pattern(
415        &self,
416        pattern: &SparqlTriplePattern,
417    ) -> Result<crate::model::pattern::TriplePattern, OxirsError> {
418        use crate::model::pattern::*;
419
420        let subject = match &pattern.subject {
421            SparqlTermPattern::Variable(v) => Some(SubjectPattern::Variable(v.clone())),
422            SparqlTermPattern::NamedNode(n) => Some(SubjectPattern::NamedNode(n.clone())),
423            SparqlTermPattern::BlankNode(b) => Some(SubjectPattern::BlankNode(b.clone())),
424            _ => None,
425        };
426
427        let predicate = match &pattern.predicate {
428            SparqlTermPattern::Variable(v) => Some(PredicatePattern::Variable(v.clone())),
429            SparqlTermPattern::NamedNode(n) => Some(PredicatePattern::NamedNode(n.clone())),
430            _ => None,
431        };
432
433        let object = match &pattern.object {
434            SparqlTermPattern::Variable(v) => Some(ObjectPattern::Variable(v.clone())),
435            SparqlTermPattern::NamedNode(n) => Some(ObjectPattern::NamedNode(n.clone())),
436            SparqlTermPattern::BlankNode(b) => Some(ObjectPattern::BlankNode(b.clone())),
437            SparqlTermPattern::Literal(l) => Some(ObjectPattern::Literal(l.clone())),
438            #[cfg(feature = "sparql-12")]
439            SparqlTermPattern::Triple(_) => {
440                // Triple patterns in object position not yet fully supported
441                None
442            }
443        };
444
445        Ok(crate::model::pattern::TriplePattern {
446            subject,
447            predicate,
448            object,
449        })
450    }
451
452    /// Convert a SPARQL algebra triple pattern to a model triple pattern
453    #[allow(dead_code)]
454    fn convert_triple_pattern(
455        &self,
456        pattern: &AlgebraTriplePattern,
457    ) -> Result<crate::model::pattern::TriplePattern, OxirsError> {
458        use crate::model::pattern::*;
459
460        let subject = match &pattern.subject {
461            TermPattern::Variable(v) => Some(SubjectPattern::Variable(v.clone())),
462            TermPattern::NamedNode(n) => Some(SubjectPattern::NamedNode(n.clone())),
463            TermPattern::BlankNode(b) => Some(SubjectPattern::BlankNode(b.clone())),
464            _ => None,
465        };
466
467        let predicate = match &pattern.predicate {
468            TermPattern::Variable(v) => Some(PredicatePattern::Variable(v.clone())),
469            TermPattern::NamedNode(n) => Some(PredicatePattern::NamedNode(n.clone())),
470            _ => None,
471        };
472
473        let object = match &pattern.object {
474            TermPattern::Variable(v) => Some(ObjectPattern::Variable(v.clone())),
475            TermPattern::NamedNode(n) => Some(ObjectPattern::NamedNode(n.clone())),
476            TermPattern::BlankNode(b) => Some(ObjectPattern::BlankNode(b.clone())),
477            TermPattern::Literal(l) => Some(ObjectPattern::Literal(l.clone())),
478        };
479
480        Ok(crate::model::pattern::TriplePattern {
481            subject,
482            predicate,
483            object,
484        })
485    }
486
487    /// Find variables that appear in both execution plans
488    fn find_join_variables(&self, _left: &ExecutionPlan, _right: &ExecutionPlan) -> Vec<Variable> {
489        // Simplified implementation - would need to analyze the plans
490        Vec::new()
491    }
492
493    /// Convert sparql_algebra::Expression to algebra::Expression
494    #[allow(clippy::only_used_in_recursion)]
495    fn convert_expression(
496        &self,
497        expr: sparql_algebra::Expression,
498    ) -> Result<AlgebraExpression, OxirsError> {
499        use sparql_algebra::Expression as SparqlExpr;
500        use AlgebraExpression as AlgebraExpr;
501
502        match expr {
503            SparqlExpr::NamedNode(n) => Ok(AlgebraExpr::Term(crate::model::Term::NamedNode(n))),
504            SparqlExpr::Literal(l) => Ok(AlgebraExpr::Term(crate::model::Term::Literal(l))),
505            SparqlExpr::Variable(v) => Ok(AlgebraExpr::Variable(v)),
506            SparqlExpr::Or(left, right) => {
507                let left_expr = self.convert_expression(*left)?;
508                let right_expr = self.convert_expression(*right)?;
509                Ok(AlgebraExpr::Or(Box::new(left_expr), Box::new(right_expr)))
510            }
511            SparqlExpr::And(left, right) => {
512                let left_expr = self.convert_expression(*left)?;
513                let right_expr = self.convert_expression(*right)?;
514                Ok(AlgebraExpr::And(Box::new(left_expr), Box::new(right_expr)))
515            }
516            SparqlExpr::Equal(left, right) => {
517                let left_expr = self.convert_expression(*left)?;
518                let right_expr = self.convert_expression(*right)?;
519                Ok(AlgebraExpr::Equal(
520                    Box::new(left_expr),
521                    Box::new(right_expr),
522                ))
523            }
524            SparqlExpr::SameTerm(left, right) => {
525                let left_expr = self.convert_expression(*left)?;
526                let right_expr = self.convert_expression(*right)?;
527                Ok(AlgebraExpr::Equal(
528                    Box::new(left_expr),
529                    Box::new(right_expr),
530                )) // Map SameTerm to Equal for now
531            }
532            SparqlExpr::Greater(left, right) => {
533                let left_expr = self.convert_expression(*left)?;
534                let right_expr = self.convert_expression(*right)?;
535                Ok(AlgebraExpr::Greater(
536                    Box::new(left_expr),
537                    Box::new(right_expr),
538                ))
539            }
540            SparqlExpr::GreaterOrEqual(left, right) => {
541                let left_expr = self.convert_expression(*left)?;
542                let right_expr = self.convert_expression(*right)?;
543                Ok(AlgebraExpr::GreaterOrEqual(
544                    Box::new(left_expr),
545                    Box::new(right_expr),
546                ))
547            }
548            SparqlExpr::Less(left, right) => {
549                let left_expr = self.convert_expression(*left)?;
550                let right_expr = self.convert_expression(*right)?;
551                Ok(AlgebraExpr::Less(Box::new(left_expr), Box::new(right_expr)))
552            }
553            SparqlExpr::LessOrEqual(left, right) => {
554                let left_expr = self.convert_expression(*left)?;
555                let right_expr = self.convert_expression(*right)?;
556                Ok(AlgebraExpr::LessOrEqual(
557                    Box::new(left_expr),
558                    Box::new(right_expr),
559                ))
560            }
561            SparqlExpr::Not(inner) => {
562                let inner_expr = self.convert_expression(*inner)?;
563                Ok(AlgebraExpr::Not(Box::new(inner_expr)))
564            }
565            _ => {
566                // For expressions not yet supported, create a placeholder
567                Err(OxirsError::Query(format!(
568                    "Expression type not yet supported in conversion: {expr:?}"
569                )))
570            }
571        }
572    }
573
574    /// Extract all variables from a graph pattern
575    fn extract_variables(&self, pattern: &SparqlGraphPattern) -> Vec<Variable> {
576        let mut variables = Vec::new();
577        self.collect_variables_from_pattern(pattern, &mut variables);
578        variables.sort_by_key(|v: &Variable| v.name().to_owned());
579        variables.dedup();
580        variables
581    }
582
583    /// Recursively collect variables from a graph pattern
584    fn collect_variables_from_pattern(
585        &self,
586        pattern: &SparqlGraphPattern,
587        variables: &mut Vec<Variable>,
588    ) {
589        match pattern {
590            SparqlGraphPattern::Bgp { patterns } => {
591                for triple_pattern in patterns {
592                    self.collect_variables_from_triple_pattern(triple_pattern, variables);
593                }
594            }
595            SparqlGraphPattern::Join { left, right } => {
596                self.collect_variables_from_pattern(left, variables);
597                self.collect_variables_from_pattern(right, variables);
598            }
599            SparqlGraphPattern::Filter { inner, .. } => {
600                self.collect_variables_from_pattern(inner, variables);
601            }
602            SparqlGraphPattern::Union { left, right } => {
603                self.collect_variables_from_pattern(left, variables);
604                self.collect_variables_from_pattern(right, variables);
605            }
606            SparqlGraphPattern::Project {
607                inner,
608                variables: proj_vars,
609            } => {
610                self.collect_variables_from_pattern(inner, variables);
611                variables.extend(proj_vars.iter().cloned());
612            }
613            SparqlGraphPattern::Distinct { inner } => {
614                self.collect_variables_from_pattern(inner, variables);
615            }
616            SparqlGraphPattern::Slice { inner, .. } => {
617                self.collect_variables_from_pattern(inner, variables);
618            }
619            _ => {
620                // Handle other pattern types as needed
621            }
622        }
623    }
624
625    /// Collect variables from a triple pattern
626    fn collect_variables_from_triple_pattern(
627        &self,
628        pattern: &SparqlTriplePattern,
629        variables: &mut Vec<Variable>,
630    ) {
631        if let SparqlTermPattern::Variable(v) = &pattern.subject {
632            variables.push(v.clone());
633        }
634        if let SparqlTermPattern::Variable(v) = &pattern.predicate {
635            variables.push(v.clone());
636        }
637        if let SparqlTermPattern::Variable(v) = &pattern.object {
638            variables.push(v.clone());
639        }
640    }
641
642    /// Instantiate a triple pattern with a solution
643    fn instantiate_triple_pattern(
644        &self,
645        pattern: &SparqlTriplePattern,
646        solution: &Solution,
647    ) -> Result<Option<crate::model::Triple>, OxirsError> {
648        use crate::model::*;
649
650        let subject = match &pattern.subject {
651            SparqlTermPattern::Variable(v) => {
652                if let Some(term) = solution.get(v) {
653                    match term {
654                        Term::NamedNode(n) => Subject::NamedNode(n.clone()),
655                        Term::BlankNode(b) => Subject::BlankNode(b.clone()),
656                        _ => return Ok(None), // Invalid subject
657                    }
658                } else {
659                    return Ok(None); // Unbound variable
660                }
661            }
662            SparqlTermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
663            SparqlTermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
664            _ => return Ok(None), // Invalid subject pattern
665        };
666
667        let predicate = match &pattern.predicate {
668            SparqlTermPattern::Variable(v) => {
669                if let Some(Term::NamedNode(n)) = solution.get(v) {
670                    Predicate::NamedNode(n.clone())
671                } else {
672                    return Ok(None); // Unbound or invalid predicate
673                }
674            }
675            SparqlTermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
676            _ => return Ok(None), // Invalid predicate pattern
677        };
678
679        let object = match &pattern.object {
680            SparqlTermPattern::Variable(v) => {
681                if let Some(term) = solution.get(v) {
682                    match term {
683                        Term::NamedNode(n) => Object::NamedNode(n.clone()),
684                        Term::BlankNode(b) => Object::BlankNode(b.clone()),
685                        Term::Literal(l) => Object::Literal(l.clone()),
686                        _ => return Ok(None), // Invalid object
687                    }
688                } else {
689                    return Ok(None); // Unbound variable
690                }
691            }
692            SparqlTermPattern::NamedNode(n) => Object::NamedNode(n.clone()),
693            SparqlTermPattern::BlankNode(b) => Object::BlankNode(b.clone()),
694            SparqlTermPattern::Literal(l) => Object::Literal(l.clone()),
695            #[cfg(feature = "sparql-12")]
696            SparqlTermPattern::Triple(_) => {
697                // Triple patterns in object position not yet fully supported
698                return Ok(None);
699            }
700        };
701
702        Ok(Some(Triple::new(subject, predicate, object)))
703    }
704
705    /// Execute a SPARQL query string against a store (async version with federation support)
706    pub async fn query_async(
707        &self,
708        query_str: &str,
709        store: &dyn Store,
710    ) -> Result<QueryResult, OxirsError> {
711        // Parse the query string
712        let parsed_query = self.parser.parse(query_str)?;
713
714        // Execute the parsed query
715        self.execute_query_async(&parsed_query, store).await
716    }
717
718    /// Execute a parsed Query object against a store (async version)
719    pub async fn execute_query_async(
720        &self,
721        query: &sparql_query::Query,
722        store: &dyn Store,
723    ) -> Result<QueryResult, OxirsError> {
724        match query {
725            sparql_query::Query::Select {
726                pattern, dataset, ..
727            } => {
728                self.execute_select_query_async(pattern, dataset.as_ref(), store)
729                    .await
730            }
731            sparql_query::Query::Ask {
732                pattern, dataset, ..
733            } => {
734                self.execute_ask_query_async(pattern, dataset.as_ref(), store)
735                    .await
736            }
737            sparql_query::Query::Construct {
738                template,
739                pattern,
740                dataset,
741                ..
742            } => {
743                self.execute_construct_query_async(template, pattern, dataset.as_ref(), store)
744                    .await
745            }
746            sparql_query::Query::Describe {
747                pattern, dataset, ..
748            } => {
749                self.execute_describe_query_async(pattern, dataset.as_ref(), store)
750                    .await
751            }
752        }
753    }
754
755    /// Execute a SELECT query (async version with federation support)
756    async fn execute_select_query_async(
757        &self,
758        pattern: &SparqlGraphPattern,
759        _dataset: Option<&QueryDataset>,
760        store: &dyn Store,
761    ) -> Result<QueryResult, OxirsError> {
762        // Check if pattern contains SERVICE clause
763        if self.contains_service_clause(pattern) {
764            // Use federation executor
765            return self.execute_federated_select(pattern, store).await;
766        }
767
768        // Fall back to regular execution
769        self.execute_select_query(pattern, _dataset, store)
770    }
771
772    /// Execute an ASK query (async version)
773    async fn execute_ask_query_async(
774        &self,
775        pattern: &SparqlGraphPattern,
776        dataset: Option<&QueryDataset>,
777        store: &dyn Store,
778    ) -> Result<QueryResult, OxirsError> {
779        if self.contains_service_clause(pattern) {
780            let result = self.execute_federated_select(pattern, store).await?;
781            if let QueryResult::Select { bindings, .. } = result {
782                return Ok(QueryResult::Ask(!bindings.is_empty()));
783            }
784        }
785        self.execute_ask_query(pattern, dataset, store)
786    }
787
788    /// Execute a CONSTRUCT query (async version)
789    async fn execute_construct_query_async(
790        &self,
791        template: &[SparqlTriplePattern],
792        pattern: &SparqlGraphPattern,
793        dataset: Option<&QueryDataset>,
794        store: &dyn Store,
795    ) -> Result<QueryResult, OxirsError> {
796        if self.contains_service_clause(pattern) {
797            return Err(OxirsError::Federation(
798                "CONSTRUCT with SERVICE is not yet fully supported".to_string(),
799            ));
800        }
801        self.execute_construct_query(template, pattern, dataset, store)
802    }
803
804    /// Execute a DESCRIBE query (async version)
805    async fn execute_describe_query_async(
806        &self,
807        pattern: &SparqlGraphPattern,
808        dataset: Option<&QueryDataset>,
809        store: &dyn Store,
810    ) -> Result<QueryResult, OxirsError> {
811        if self.contains_service_clause(pattern) {
812            return Err(OxirsError::Federation(
813                "DESCRIBE with SERVICE is not yet fully supported".to_string(),
814            ));
815        }
816        self.execute_describe_query(pattern, dataset, store)
817    }
818
819    /// Check if a pattern contains a SERVICE clause
820    fn contains_service_clause(&self, pattern: &SparqlGraphPattern) -> bool {
821        
822
823        // Convert SparqlGraphPattern to algebra::GraphPattern for checking
824        // For now, we'll do a simple string-based check
825        // TODO: Implement proper pattern traversal
826        matches!(pattern, SparqlGraphPattern::Service { .. })
827            || self.pattern_contains_service_recursive(pattern)
828    }
829
830    /// Recursively check for SERVICE in nested patterns
831    fn pattern_contains_service_recursive(&self, pattern: &SparqlGraphPattern) -> bool {
832        match pattern {
833            SparqlGraphPattern::Service { .. } => true,
834            SparqlGraphPattern::Join { left, right }
835            | SparqlGraphPattern::Union { left, right } => {
836                self.pattern_contains_service_recursive(left)
837                    || self.pattern_contains_service_recursive(right)
838            }
839            SparqlGraphPattern::Filter { inner, .. }
840            | SparqlGraphPattern::Distinct { inner }
841            | SparqlGraphPattern::Reduced { inner }
842            | SparqlGraphPattern::Project { inner, .. } => {
843                self.pattern_contains_service_recursive(inner)
844            }
845            SparqlGraphPattern::LeftJoin { left, right, .. } => {
846                self.pattern_contains_service_recursive(left)
847                    || self.pattern_contains_service_recursive(right)
848            }
849            _ => false,
850        }
851    }
852
853    /// Execute a federated SELECT query
854    async fn execute_federated_select(
855        &self,
856        pattern: &SparqlGraphPattern,
857        store: &dyn Store,
858    ) -> Result<QueryResult, OxirsError> {
859        let federation_executor = self.federation_executor.as_ref().ok_or_else(|| {
860            OxirsError::Federation("Federation executor not available".to_string())
861        })?;
862
863        // Start with empty bindings from local store
864        let local_bindings = Vec::new();
865
866        // Execute the federated pattern
867        let bindings = self
868            .execute_pattern_with_federation(pattern, local_bindings, federation_executor, store)
869            .await?;
870
871        // Extract variable names
872        let variables = self
873            .extract_variables(pattern)
874            .into_iter()
875            .map(|v| v.name().to_string())
876            .collect();
877
878        Ok(QueryResult::Select {
879            variables,
880            bindings,
881        })
882    }
883
884    /// Execute a pattern with federation support
885    fn execute_pattern_with_federation<'a>(
886        &'a self,
887        pattern: &'a SparqlGraphPattern,
888        current_bindings: Vec<HashMap<String, Term>>,
889        federation_executor: &'a crate::federation::FederationExecutor,
890        store: &'a dyn Store,
891    ) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Term>>, OxirsError>> + 'a>> {
892        Box::pin(async move {
893            let current_bindings = current_bindings;
894            match pattern {
895                SparqlGraphPattern::Service {
896                    name,
897                    inner,
898                    silent,
899                } => {
900                    // Execute SERVICE clause
901                    let remote_bindings = federation_executor
902                        .execute_service(name, inner, *silent, &current_bindings)
903                        .await?;
904
905                    // Merge local and remote bindings
906                    if current_bindings.is_empty() {
907                        Ok(remote_bindings)
908                    } else {
909                        Ok(federation_executor.merge_bindings(current_bindings, remote_bindings))
910                    }
911                }
912                SparqlGraphPattern::Join { left, right } => {
913                    // Execute left side first
914                    let left_bindings = self
915                        .execute_pattern_with_federation(
916                            left,
917                            current_bindings,
918                            federation_executor,
919                            store,
920                        )
921                        .await?;
922
923                    // Then execute right side with left bindings
924                    self.execute_pattern_with_federation(
925                        right,
926                        left_bindings,
927                        federation_executor,
928                        store,
929                    )
930                    .await
931                }
932                _ => {
933                    // For non-federated patterns, use regular execution
934                    let executor = QueryExecutor::new(store);
935                    let plan = self.pattern_to_plan(pattern)?;
936                    let solutions = executor.execute(&plan)?;
937
938                    let bindings: Vec<HashMap<String, Term>> = solutions
939                        .into_iter()
940                        .take(self.executor_config.max_results)
941                        .map(|sol| {
942                            sol.iter()
943                                .map(|(var, term)| (var.name().to_string(), term.clone()))
944                                .collect()
945                        })
946                        .collect();
947
948                    if current_bindings.is_empty() {
949                        Ok(bindings)
950                    } else {
951                        // Merge current and new bindings
952                        Ok(federation_executor.merge_bindings(current_bindings, bindings))
953                    }
954                }
955            }
956        })
957    }
958
959    /// Check if a triple involves a specific term
960    fn triple_involves_term(&self, triple: &crate::model::Triple, term: &Term) -> bool {
961        match term {
962            Term::NamedNode(n) => {
963                matches!(triple.subject(), Subject::NamedNode(sn) if sn == n)
964                    || matches!(triple.predicate(), Predicate::NamedNode(pn) if pn == n)
965                    || matches!(triple.object(), Object::NamedNode(on) if on == n)
966            }
967            Term::BlankNode(b) => {
968                matches!(triple.subject(), Subject::BlankNode(sb) if sb == b)
969                    || matches!(triple.object(), Object::BlankNode(ob) if ob == b)
970            }
971            Term::Literal(l) => {
972                matches!(triple.object(), Object::Literal(ol) if ol == l)
973            }
974            _ => false,
975        }
976    }
977}