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_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
42// Re-export property function registry types
43pub use property_function_registry::{
44    PropertyFunction, PropertyFunctionArg, PropertyFunctionBinding, PropertyFunctionFactory,
45    PropertyFunctionMetadata, PropertyFunctionRegistry, PropertyFunctionResult,
46};
47
48// Re-export the enhanced SPARQL algebra and query types from sparql_algebra
49pub 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
56// Re-export execution plan types
57pub use plan::ExecutionPlan;
58
59// Re-export advanced statistics types
60pub use advanced_statistics::{AdvancedStatistics, AdvancedStatisticsCollector, PatternExecution};
61
62// Re-export algebra types (without Query to avoid conflict)
63// Use explicit aliases to avoid conflicts
64pub 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
112// TODO: Temporary compatibility layer for SHACL module
113pub 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
122// Import TermPattern for internal usage
123use algebra::TermPattern;
124
125/// Type alias for federated query execution future
126type FederatedQueryFuture<'a> =
127    Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Term>>, OxirsError>> + 'a>>;
128
129/// Simplified QueryResult for SHACL compatibility
130#[derive(Debug, Clone)]
131pub enum QueryResult {
132    /// SELECT query results
133    Select {
134        variables: Vec<String>,
135        bindings: Vec<HashMap<String, Term>>,
136    },
137    /// ASK query results
138    Ask(bool),
139    /// CONSTRUCT query results
140    Construct(Vec<crate::model::Triple>),
141}
142
143/// Simplified QueryEngine for SHACL compatibility
144pub struct QueryEngine {
145    /// Query parser for converting SPARQL strings to Query objects
146    parser: parser::SparqlParser,
147    /// Query executor for executing plans
148    executor_config: QueryExecutorConfig,
149    /// Federation executor for SERVICE clause support
150    federation_executor: Option<crate::federation::FederationExecutor>,
151}
152
153impl Default for QueryEngine {
154    fn default() -> Self {
155        Self::new()
156    }
157}
158
159/// Configuration for query execution
160#[derive(Debug, Clone)]
161pub struct QueryExecutorConfig {
162    /// Maximum number of results to return
163    pub max_results: usize,
164    /// Query timeout in milliseconds
165    pub timeout_ms: Option<u64>,
166    /// Enable query optimization
167    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    /// Create a new query engine
182    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    /// Create a new query engine with custom configuration
191    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    /// Enable federation support
200    pub fn with_federation(mut self) -> Self {
201        self.federation_executor = crate::federation::FederationExecutor::new().ok();
202        self
203    }
204
205    /// Disable federation support
206    pub fn without_federation(mut self) -> Self {
207        self.federation_executor = None;
208        self
209    }
210
211    /// Execute a SPARQL query string against a store
212    pub fn query(&self, query_str: &str, store: &dyn Store) -> Result<QueryResult, OxirsError> {
213        // Parse the query string
214        let parsed_query = self.parser.parse(query_str)?;
215
216        // Execute the parsed query
217        self.execute_query(&parsed_query, store)
218    }
219
220    /// Execute a parsed Query object against a store
221    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    /// Execute a SELECT query
246    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        // Convert graph pattern to execution plan
255        let plan = self.pattern_to_plan(pattern)?;
256
257        // Execute the plan
258        let solutions = executor.execute(&plan)?;
259
260        // Extract variable names and convert solutions
261        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    /// Execute an ASK query
286    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        // Convert graph pattern to execution plan
295        let plan = self.pattern_to_plan(pattern)?;
296
297        // Execute the plan
298        let solutions = executor.execute(&plan)?;
299
300        // ASK query returns true if there are any solutions
301        Ok(QueryResult::Ask(!solutions.is_empty()))
302    }
303
304    /// Execute a CONSTRUCT query
305    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        // Convert graph pattern to execution plan
315        let plan = self.pattern_to_plan(pattern)?;
316
317        // Execute the plan
318        let solutions = executor.execute(&plan)?;
319
320        // Construct triples from template and solutions
321        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    /// Execute a DESCRIBE query
334    fn execute_describe_query(
335        &self,
336        pattern: &SparqlGraphPattern,
337        _dataset: Option<&QueryDataset>,
338        store: &dyn Store,
339    ) -> Result<QueryResult, OxirsError> {
340        // For now, treat DESCRIBE like CONSTRUCT *
341        // This is a simplified implementation
342        let executor = QueryExecutor::new(store);
343
344        // Convert graph pattern to execution plan
345        let plan = self.pattern_to_plan(pattern)?;
346
347        // Execute the plan
348        let solutions = executor.execute(&plan)?;
349
350        // Get all triples involving the found entities
351        let mut triples = Vec::new();
352        for solution in solutions.into_iter().take(self.executor_config.max_results) {
353            // For each bound entity, get all triples where it appears
354            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    /// Convert a graph pattern to an execution plan
377    fn pattern_to_plan(&self, pattern: &SparqlGraphPattern) -> Result<ExecutionPlan, OxirsError> {
378        match pattern {
379            SparqlGraphPattern::Bgp { patterns } => {
380                if patterns.len() == 1 {
381                    // Single triple pattern
382                    Ok(ExecutionPlan::TripleScan {
383                        pattern: self.convert_sparql_triple_pattern(&patterns[0])?,
384                    })
385                } else {
386                    // Multiple patterns - join them
387                    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                        // Find join variables
397                        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                // Convert sparql_algebra::Expression to algebra::Expression
423                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                // For unsupported patterns, return an error for now
465                Err(OxirsError::Query(format!(
466                    "Unsupported graph pattern type: {pattern:?}"
467                )))
468            }
469        }
470    }
471
472    /// Convert a SPARQL triple pattern to a model triple pattern
473    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                // Triple patterns in object position not yet fully supported
500                None
501            }
502        };
503
504        Ok(crate::model::pattern::TriplePattern {
505            subject,
506            predicate,
507            object,
508        })
509    }
510
511    /// Convert a SPARQL algebra triple pattern to a model triple pattern
512    #[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    /// Find variables that appear in both execution plans
550    fn find_join_variables(&self, _left: &ExecutionPlan, _right: &ExecutionPlan) -> Vec<Variable> {
551        // Simplified implementation - would need to analyze the plans
552        Vec::new()
553    }
554
555    /// Convert sparql_algebra::Expression to algebra::Expression
556    #[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                )) // Map SameTerm to Equal for now
593            }
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                // For expressions not yet supported, create a placeholder
629                Err(OxirsError::Query(format!(
630                    "Expression type not yet supported in conversion: {expr:?}"
631                )))
632            }
633        }
634    }
635
636    /// Extract all variables from a graph pattern
637    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    /// Recursively collect variables from a graph pattern
646    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                // Handle other pattern types as needed
683            }
684        }
685    }
686
687    /// Collect variables from a triple pattern
688    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    /// Instantiate a triple pattern with a solution
705    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), // Invalid subject
719                    }
720                } else {
721                    return Ok(None); // Unbound variable
722                }
723            }
724            SparqlTermPattern::NamedNode(n) => Subject::NamedNode(n.clone()),
725            SparqlTermPattern::BlankNode(b) => Subject::BlankNode(b.clone()),
726            _ => return Ok(None), // Invalid subject pattern
727        };
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); // Unbound or invalid predicate
735                }
736            }
737            SparqlTermPattern::NamedNode(n) => Predicate::NamedNode(n.clone()),
738            _ => return Ok(None), // Invalid predicate pattern
739        };
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), // Invalid object
749                    }
750                } else {
751                    return Ok(None); // Unbound variable
752                }
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                // Triple patterns in object position not yet fully supported
760                return Ok(None);
761            }
762        };
763
764        Ok(Some(Triple::new(subject, predicate, object)))
765    }
766
767    /// Execute a SPARQL query string against a store (async version with federation support)
768    pub async fn query_async(
769        &self,
770        query_str: &str,
771        store: &dyn Store,
772    ) -> Result<QueryResult, OxirsError> {
773        // Parse the query string
774        let parsed_query = self.parser.parse(query_str)?;
775
776        // Execute the parsed query
777        self.execute_query_async(&parsed_query, store).await
778    }
779
780    /// Execute a parsed Query object against a store (async version)
781    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    /// Execute a SELECT query (async version with federation support)
818    async fn execute_select_query_async(
819        &self,
820        pattern: &SparqlGraphPattern,
821        _dataset: Option<&QueryDataset>,
822        store: &dyn Store,
823    ) -> Result<QueryResult, OxirsError> {
824        // Check if pattern contains SERVICE clause
825        if self.contains_service_clause(pattern) {
826            // Use federation executor
827            return self.execute_federated_select(pattern, store).await;
828        }
829
830        // Fall back to regular execution
831        self.execute_select_query(pattern, _dataset, store)
832    }
833
834    /// Execute an ASK query (async version)
835    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    /// Execute a CONSTRUCT query (async version)
851    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    /// Execute a DESCRIBE query (async version)
867    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    /// Check if a pattern contains a SERVICE clause
882    fn contains_service_clause(&self, pattern: &SparqlGraphPattern) -> bool {
883        // Check for SERVICE patterns using recursive pattern traversal
884        matches!(pattern, SparqlGraphPattern::Service { .. })
885            || Self::pattern_contains_service_recursive(pattern)
886    }
887
888    /// Recursively check for SERVICE in nested patterns
889    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    /// Execute a federated SELECT query
912    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        // Start with empty bindings from local store
922        let local_bindings = Vec::new();
923
924        // Execute the federated pattern
925        let bindings = self
926            .execute_pattern_with_federation(pattern, local_bindings, federation_executor, store)
927            .await?;
928
929        // Extract variable names
930        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    /// Execute a pattern with federation support
943    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                    // Execute SERVICE clause
959                    let remote_bindings = federation_executor
960                        .execute_service(name, inner, *silent, &current_bindings)
961                        .await?;
962
963                    // Merge local and remote bindings
964                    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                    // Execute left side first
972                    let left_bindings = self
973                        .execute_pattern_with_federation(
974                            left,
975                            current_bindings,
976                            federation_executor,
977                            store,
978                        )
979                        .await?;
980
981                    // Then execute right side with left bindings
982                    self.execute_pattern_with_federation(
983                        right,
984                        left_bindings,
985                        federation_executor,
986                        store,
987                    )
988                    .await
989                }
990                _ => {
991                    // For non-federated patterns, use regular execution
992                    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                        // Merge current and new bindings
1010                        Ok(federation_executor.merge_bindings(current_bindings, bindings))
1011                    }
1012                }
1013            }
1014        })
1015    }
1016
1017    /// Check if a triple involves a specific term
1018    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}