Skip to main content

oxirs_core/query/
mod.rs

1//! SPARQL query processing module with full SciRS2 integration
2
3pub 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
33// Re-export property function registry types
34pub use property_function_registry::{
35    PropertyFunction, PropertyFunctionArg, PropertyFunctionBinding, PropertyFunctionFactory,
36    PropertyFunctionMetadata, PropertyFunctionRegistry, PropertyFunctionResult,
37};
38
39// Re-export the enhanced SPARQL algebra and query types from sparql_algebra
40pub 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
47// Re-export execution plan types
48pub use plan::ExecutionPlan;
49
50// Re-export advanced statistics types
51pub use advanced_statistics::{AdvancedStatistics, AdvancedStatisticsCollector, PatternExecution};
52
53// Re-export algebra types (without Query to avoid conflict)
54// Use explicit aliases to avoid conflicts
55pub 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
103// TODO: Temporary compatibility layer for SHACL module
104pub 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
113// Import TermPattern for internal usage
114use algebra::TermPattern;
115
116/// Type alias for federated query execution future
117type FederatedQueryFuture<'a> =
118    Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Term>>, OxirsError>> + 'a>>;
119
120/// Simplified QueryResult for SHACL compatibility
121#[derive(Debug, Clone)]
122pub enum QueryResult {
123    /// SELECT query results
124    Select {
125        variables: Vec<String>,
126        bindings: Vec<HashMap<String, Term>>,
127    },
128    /// ASK query results
129    Ask(bool),
130    /// CONSTRUCT query results
131    Construct(Vec<crate::model::Triple>),
132}
133
134/// Simplified QueryEngine for SHACL compatibility
135pub struct QueryEngine {
136    /// Query parser for converting SPARQL strings to Query objects
137    parser: parser::SparqlParser,
138    /// Query executor for executing plans
139    executor_config: QueryExecutorConfig,
140    /// Federation executor for SERVICE clause support
141    federation_executor: Option<crate::federation::FederationExecutor>,
142}
143
144impl Default for QueryEngine {
145    fn default() -> Self {
146        Self::new()
147    }
148}
149
150/// Configuration for query execution
151#[derive(Debug, Clone)]
152pub struct QueryExecutorConfig {
153    /// Maximum number of results to return
154    pub max_results: usize,
155    /// Query timeout in milliseconds
156    pub timeout_ms: Option<u64>,
157    /// Enable query optimization
158    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    /// Create a new query engine
173    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    /// Create a new query engine with custom configuration
182    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    /// Enable federation support
191    pub fn with_federation(mut self) -> Self {
192        self.federation_executor = crate::federation::FederationExecutor::new().ok();
193        self
194    }
195
196    /// Disable federation support
197    pub fn without_federation(mut self) -> Self {
198        self.federation_executor = None;
199        self
200    }
201
202    /// Execute a SPARQL query string against a store
203    pub fn query(&self, query_str: &str, store: &dyn Store) -> Result<QueryResult, OxirsError> {
204        // Parse the query string
205        let parsed_query = self.parser.parse(query_str)?;
206
207        // Execute the parsed query
208        self.execute_query(&parsed_query, store)
209    }
210
211    /// Execute a parsed Query object against a store
212    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    /// Execute a SELECT query
237    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        // Convert graph pattern to execution plan
246        let plan = self.pattern_to_plan(pattern)?;
247
248        // Execute the plan
249        let solutions = executor.execute(&plan)?;
250
251        // Extract variable names and convert solutions
252        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    /// Execute an ASK query
277    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        // 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        // ASK query returns true if there are any solutions
292        Ok(QueryResult::Ask(!solutions.is_empty()))
293    }
294
295    /// Execute a CONSTRUCT query
296    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        // Convert graph pattern to execution plan
306        let plan = self.pattern_to_plan(pattern)?;
307
308        // Execute the plan
309        let solutions = executor.execute(&plan)?;
310
311        // Construct triples from template and solutions
312        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    /// Execute a DESCRIBE query
325    fn execute_describe_query(
326        &self,
327        pattern: &SparqlGraphPattern,
328        _dataset: Option<&QueryDataset>,
329        store: &dyn Store,
330    ) -> Result<QueryResult, OxirsError> {
331        // For now, treat DESCRIBE like CONSTRUCT *
332        // This is a simplified implementation
333        let executor = QueryExecutor::new(store);
334
335        // Convert graph pattern to execution plan
336        let plan = self.pattern_to_plan(pattern)?;
337
338        // Execute the plan
339        let solutions = executor.execute(&plan)?;
340
341        // Get all triples involving the found entities
342        let mut triples = Vec::new();
343        for solution in solutions.into_iter().take(self.executor_config.max_results) {
344            // For each bound entity, get all triples where it appears
345            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    /// Convert a graph pattern to an execution plan
368    fn pattern_to_plan(&self, pattern: &SparqlGraphPattern) -> Result<ExecutionPlan, OxirsError> {
369        match pattern {
370            SparqlGraphPattern::Bgp { patterns } => {
371                if patterns.len() == 1 {
372                    // Single triple pattern
373                    Ok(ExecutionPlan::TripleScan {
374                        pattern: self.convert_sparql_triple_pattern(&patterns[0])?,
375                    })
376                } else {
377                    // Multiple patterns - join them
378                    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                        // Find join variables
388                        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                // Convert sparql_algebra::Expression to algebra::Expression
414                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                // For unsupported patterns, return an error for now
456                Err(OxirsError::Query(format!(
457                    "Unsupported graph pattern type: {pattern:?}"
458                )))
459            }
460        }
461    }
462
463    /// Convert a SPARQL triple pattern to a model triple pattern
464    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                // Triple patterns in object position not yet fully supported
491                None
492            }
493        };
494
495        Ok(crate::model::pattern::TriplePattern {
496            subject,
497            predicate,
498            object,
499        })
500    }
501
502    /// Convert a SPARQL algebra triple pattern to a model triple pattern
503    #[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    /// Find variables that appear in both execution plans
541    fn find_join_variables(&self, _left: &ExecutionPlan, _right: &ExecutionPlan) -> Vec<Variable> {
542        // Simplified implementation - would need to analyze the plans
543        Vec::new()
544    }
545
546    /// Convert sparql_algebra::Expression to algebra::Expression
547    #[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                )) // Map SameTerm to Equal for now
584            }
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                // For expressions not yet supported, create a placeholder
620                Err(OxirsError::Query(format!(
621                    "Expression type not yet supported in conversion: {expr:?}"
622                )))
623            }
624        }
625    }
626
627    /// Extract all variables from a graph pattern
628    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    /// Recursively collect variables from a graph pattern
637    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                // Handle other pattern types as needed
674            }
675        }
676    }
677
678    /// Collect variables from a triple pattern
679    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    /// Instantiate a triple pattern with a solution
696    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), // Invalid subject
710                    }
711                } else {
712                    return Ok(None); // Unbound variable
713                }
714            }
715            SparqlTermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
716            SparqlTermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
717            _ => return Ok(None), // Invalid subject pattern
718        };
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); // Unbound or invalid predicate
726                }
727            }
728            SparqlTermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
729            _ => return Ok(None), // Invalid predicate pattern
730        };
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), // Invalid object
740                    }
741                } else {
742                    return Ok(None); // Unbound variable
743                }
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                // Triple patterns in object position not yet fully supported
751                return Ok(None);
752            }
753        };
754
755        Ok(Some(Triple::new(subject, predicate, object)))
756    }
757
758    /// Execute a SPARQL query string against a store (async version with federation support)
759    pub async fn query_async(
760        &self,
761        query_str: &str,
762        store: &dyn Store,
763    ) -> Result<QueryResult, OxirsError> {
764        // Parse the query string
765        let parsed_query = self.parser.parse(query_str)?;
766
767        // Execute the parsed query
768        self.execute_query_async(&parsed_query, store).await
769    }
770
771    /// Execute a parsed Query object against a store (async version)
772    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    /// Execute a SELECT query (async version with federation support)
809    async fn execute_select_query_async(
810        &self,
811        pattern: &SparqlGraphPattern,
812        _dataset: Option<&QueryDataset>,
813        store: &dyn Store,
814    ) -> Result<QueryResult, OxirsError> {
815        // Check if pattern contains SERVICE clause
816        if self.contains_service_clause(pattern) {
817            // Use federation executor
818            return self.execute_federated_select(pattern, store).await;
819        }
820
821        // Fall back to regular execution
822        self.execute_select_query(pattern, _dataset, store)
823    }
824
825    /// Execute an ASK query (async version)
826    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    /// Execute a CONSTRUCT query (async version)
842    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    /// Execute a DESCRIBE query (async version)
858    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    /// Check if a pattern contains a SERVICE clause
873    fn contains_service_clause(&self, pattern: &SparqlGraphPattern) -> bool {
874        // Check for SERVICE patterns using recursive pattern traversal
875        matches!(pattern, SparqlGraphPattern::Service { .. })
876            || Self::pattern_contains_service_recursive(pattern)
877    }
878
879    /// Recursively check for SERVICE in nested patterns
880    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    /// Execute a federated SELECT query
903    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        // Start with empty bindings from local store
913        let local_bindings = Vec::new();
914
915        // Execute the federated pattern
916        let bindings = self
917            .execute_pattern_with_federation(pattern, local_bindings, federation_executor, store)
918            .await?;
919
920        // Extract variable names
921        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    /// Execute a pattern with federation support
934    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                    // Execute SERVICE clause
950                    let remote_bindings = federation_executor
951                        .execute_service(name, inner, *silent, &current_bindings)
952                        .await?;
953
954                    // Merge local and remote bindings
955                    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                    // Execute left side first
963                    let left_bindings = self
964                        .execute_pattern_with_federation(
965                            left,
966                            current_bindings,
967                            federation_executor,
968                            store,
969                        )
970                        .await?;
971
972                    // Then execute right side with left bindings
973                    self.execute_pattern_with_federation(
974                        right,
975                        left_bindings,
976                        federation_executor,
977                        store,
978                    )
979                    .await
980                }
981                _ => {
982                    // For non-federated patterns, use regular execution
983                    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                        // Merge current and new bindings
1001                        Ok(federation_executor.merge_bindings(current_bindings, bindings))
1002                    }
1003                }
1004            }
1005        })
1006    }
1007
1008    /// Check if a triple involves a specific term
1009    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}