1#![allow(dead_code)]
14
15use crate::model::pattern::TriplePattern;
16use crate::model::{Object, Predicate, Subject, Term, Triple, Variable};
17use crate::query::algebra::TermPattern;
18use crate::query::plan::ExecutionPlan;
19use crate::OxirsError;
20use ahash::AHashMap;
21use lru::LruCache;
22use std::collections::HashMap;
23use std::num::NonZeroUsize;
24use std::sync::{Arc, Mutex, RwLock};
25use std::time::{Duration, Instant};
26
27pub struct JitCompiler {
29 compiled_cache: Arc<RwLock<CompiledQueryCache>>,
31 execution_stats: Arc<RwLock<ExecutionStatistics>>,
33 result_cache: Arc<Mutex<LruCache<QueryHash, CachedQueryResult>>>,
35 cardinality_estimates: Arc<RwLock<AHashMap<String, CardinalityEstimate>>>,
37 pattern_optimizers: Arc<PatternOptimizers>,
39 config: JitConfig,
41}
42
43#[derive(Clone)]
45struct CachedQueryResult {
46 output: QueryOutput,
47 cached_at: Instant,
48 ttl: Duration,
49}
50
51#[derive(Debug, Clone)]
53struct CardinalityEstimate {
54 estimated_count: usize,
55 confidence: f64, last_updated: Instant,
57}
58
59struct PatternOptimizers {
61 star_optimizer: StarPatternOptimizer,
63 chain_optimizer: ChainPatternOptimizer,
65 path_optimizer: PathPatternOptimizer,
67}
68
69struct StarPatternOptimizer;
71
72struct ChainPatternOptimizer;
74
75struct PathPatternOptimizer;
77
78#[derive(Debug, Clone)]
80pub struct JitConfig {
81 pub compilation_threshold: usize,
83 pub max_cache_size: usize,
85 pub aggressive_opts: bool,
87 pub target_features: TargetFeatures,
89 pub enable_result_cache: bool,
91 pub result_cache_size: usize,
93 pub result_cache_ttl: Duration,
95 pub enable_adaptive_optimization: bool,
97 pub enable_pattern_optimizers: bool,
99}
100
101impl Default for JitConfig {
102 fn default() -> Self {
103 Self {
104 compilation_threshold: 10,
105 max_cache_size: 100 * 1024 * 1024, aggressive_opts: true,
107 target_features: TargetFeatures::default(),
108 enable_result_cache: true,
109 result_cache_size: 1000,
110 result_cache_ttl: Duration::from_secs(300), enable_adaptive_optimization: true,
112 enable_pattern_optimizers: true,
113 }
114 }
115}
116
117#[derive(Debug, Clone)]
119pub struct TargetFeatures {
120 pub avx2: bool,
122 pub avx512: bool,
124 pub bmi2: bool,
126 pub vectorize: bool,
128}
129
130impl Default for TargetFeatures {
131 fn default() -> Self {
132 Self {
133 avx2: cfg!(target_feature = "avx2"),
134 avx512: cfg!(target_feature = "avx512f"),
135 bmi2: cfg!(target_feature = "bmi2"),
136 vectorize: true,
137 }
138 }
139}
140
141struct CompiledQueryCache {
143 queries: HashMap<QueryHash, CompiledQuery>,
145 total_size: usize,
147 access_order: Vec<QueryHash>,
149}
150
151struct CompiledQuery {
153 function: QueryFunction,
155 code_size: usize,
157 compile_time: Duration,
159 last_accessed: Instant,
161 execution_count: usize,
163}
164
165type QueryHash = u64;
167
168type QueryFunction = Arc<dyn Fn(&QueryContext) -> Result<QueryOutput, OxirsError> + Send + Sync>;
170
171pub struct QueryContext {
173 pub data: Arc<GraphData>,
175 pub bindings: HashMap<Variable, Term>,
177 pub limits: ExecutionLimits,
179}
180
181pub struct GraphData {
183 pub triples: Vec<Triple>,
185 pub indexes: QueryIndexes,
187}
188
189pub struct QueryIndexes {
191 pub by_subject: HashMap<Subject, Vec<usize>>,
193 pub by_predicate: HashMap<Predicate, Vec<usize>>,
195 pub by_object: HashMap<Object, Vec<usize>>,
197}
198
199#[derive(Debug, Clone)]
201pub struct ExecutionLimits {
202 pub max_results: usize,
204 pub timeout: Duration,
206 pub memory_limit: usize,
208}
209
210#[derive(Clone)]
212pub struct QueryOutput {
213 pub bindings: Vec<HashMap<Variable, Term>>,
215 pub stats: QueryStats,
217}
218
219#[derive(Debug, Clone)]
221pub struct QueryStats {
222 pub triples_scanned: usize,
224 pub results_count: usize,
226 pub execution_time: Duration,
228 pub memory_used: usize,
230}
231
232struct ExecutionStatistics {
234 query_counts: HashMap<QueryHash, usize>,
236 query_times: HashMap<QueryHash, Vec<Duration>>,
238 hot_threshold: usize,
240}
241
242impl Default for JitCompiler {
243 fn default() -> Self {
244 Self::with_config(JitConfig::default())
245 }
246}
247
248impl JitCompiler {
249 pub fn new() -> Self {
251 Self::default()
252 }
253
254 pub fn with_config(config: JitConfig) -> Self {
256 let cache_size = NonZeroUsize::new(config.result_cache_size)
257 .unwrap_or(NonZeroUsize::new(1000).expect("constant is non-zero"));
258
259 Self {
260 compiled_cache: Arc::new(RwLock::new(CompiledQueryCache::new())),
261 execution_stats: Arc::new(RwLock::new(ExecutionStatistics::new(
262 config.compilation_threshold,
263 ))),
264 result_cache: Arc::new(Mutex::new(LruCache::new(cache_size))),
265 cardinality_estimates: Arc::new(RwLock::new(AHashMap::new())),
266 pattern_optimizers: Arc::new(PatternOptimizers {
267 star_optimizer: StarPatternOptimizer,
268 chain_optimizer: ChainPatternOptimizer,
269 path_optimizer: PathPatternOptimizer,
270 }),
271 config,
272 }
273 }
274
275 pub fn execute(
277 &self,
278 plan: &ExecutionPlan,
279 context: QueryContext,
280 ) -> Result<QueryOutput, OxirsError> {
281 let hash = self.hash_plan(plan);
282
283 if self.config.enable_result_cache {
285 if let Some(cached) = self.get_cached_result(hash) {
286 return Ok(cached.output.clone());
287 }
288 }
289
290 if let Some(compiled) = self.get_compiled(hash) {
292 let result = (compiled)(&context)?;
293
294 if self.config.enable_result_cache {
296 self.cache_result(hash, result.clone());
297 }
298
299 return Ok(result);
300 }
301
302 let optimized_plan = if self.config.enable_pattern_optimizers {
304 self.optimize_pattern(plan)?
305 } else {
306 plan.clone()
307 };
308
309 let start = Instant::now();
311 let result = self.execute_interpreted(&optimized_plan, &context)?;
312 let execution_time = start.elapsed();
313
314 self.update_stats(hash, execution_time);
316
317 if self.config.enable_adaptive_optimization {
319 self.update_cardinality_estimates(plan, &result);
320 }
321
322 if self.should_compile(hash) {
324 self.compile_plan(&optimized_plan, hash)?;
325 }
326
327 if self.config.enable_result_cache {
329 self.cache_result(hash, result.clone());
330 }
331
332 Ok(result)
333 }
334
335 fn get_cached_result(&self, hash: QueryHash) -> Option<CachedQueryResult> {
337 let mut cache = self.result_cache.lock().ok()?;
338 let cached = cache.get(&hash)?;
339
340 if cached.cached_at.elapsed() > cached.ttl {
342 cache.pop(&hash);
343 return None;
344 }
345
346 Some(cached.clone())
347 }
348
349 fn cache_result(&self, hash: QueryHash, output: QueryOutput) {
351 if let Ok(mut cache) = self.result_cache.lock() {
352 cache.put(
353 hash,
354 CachedQueryResult {
355 output,
356 cached_at: Instant::now(),
357 ttl: self.config.result_cache_ttl,
358 },
359 );
360 }
361 }
362
363 fn optimize_pattern(&self, plan: &ExecutionPlan) -> Result<ExecutionPlan, OxirsError> {
365 if !self.config.enable_pattern_optimizers {
366 return Ok(plan.clone());
367 }
368
369 match self.detect_query_pattern(plan) {
371 QueryPattern::StarPattern(patterns) => {
372 tracing::debug!("Detected star pattern with {} arms", patterns.len());
373 self.pattern_optimizers
374 .star_optimizer
375 .optimize(plan, &patterns)
376 }
377 QueryPattern::ChainPattern(chain_info) => {
378 tracing::debug!("Detected chain pattern with {} links", chain_info.len());
379 self.pattern_optimizers
380 .chain_optimizer
381 .optimize(plan, &chain_info)
382 }
383 QueryPattern::PathPattern(path_info) => {
384 tracing::debug!("Detected path pattern: {:?}", path_info);
385 self.pattern_optimizers
386 .path_optimizer
387 .optimize(plan, &path_info)
388 }
389 QueryPattern::SelectivePattern(selectivity) => {
390 tracing::debug!(
391 "Detected selective pattern (selectivity: {:.2})",
392 selectivity
393 );
394 self.optimize_selective_pattern(plan, selectivity)
395 }
396 QueryPattern::Complex => {
397 tracing::debug!("Complex pattern - applying general optimizations");
398 self.optimize_complex_pattern(plan)
399 }
400 QueryPattern::Simple => {
401 Ok(plan.clone())
403 }
404 }
405 }
406
407 fn detect_query_pattern(&self, plan: &ExecutionPlan) -> QueryPattern {
409 let patterns = self.extract_triple_patterns(plan);
411
412 if let Some(star_patterns) = self.detect_star_pattern(&patterns) {
414 return QueryPattern::StarPattern(star_patterns);
415 }
416
417 if let Some(chain) = self.detect_chain_pattern(&patterns) {
419 return QueryPattern::ChainPattern(chain);
420 }
421
422 if let Some(path_info) = self.detect_path_pattern(plan) {
424 return QueryPattern::PathPattern(path_info);
425 }
426
427 if let Some(selectivity) = self.calculate_selectivity(plan) {
429 if selectivity > 0.8 {
430 return QueryPattern::SelectivePattern(selectivity);
432 }
433 }
434
435 if patterns.len() > 3 {
437 QueryPattern::Complex
438 } else {
439 QueryPattern::Simple
440 }
441 }
442
443 fn extract_triple_patterns(&self, plan: &ExecutionPlan) -> Vec<TriplePattern> {
445 let _self = self;
447 let mut patterns = Vec::new();
448
449 match plan {
450 ExecutionPlan::TripleScan { pattern } => {
451 patterns.push(pattern.clone());
452 }
453 ExecutionPlan::HashJoin { left, right, .. } => {
454 patterns.extend(self.extract_triple_patterns(left));
455 patterns.extend(self.extract_triple_patterns(right));
456 }
457 ExecutionPlan::Union { left, right } => {
458 patterns.extend(self.extract_triple_patterns(left));
459 patterns.extend(self.extract_triple_patterns(right));
460 }
461 ExecutionPlan::Filter { input, .. }
462 | ExecutionPlan::Project { input, .. }
463 | ExecutionPlan::Distinct { input, .. }
464 | ExecutionPlan::Sort { input, .. }
465 | ExecutionPlan::Limit { input, .. } => {
466 patterns.extend(self.extract_triple_patterns(input));
467 }
468 }
469
470 patterns
471 }
472
473 fn detect_star_pattern(&self, patterns: &[TriplePattern]) -> Option<Vec<TriplePattern>> {
475 if patterns.len() < 2 {
476 return None;
477 }
478
479 use crate::model::pattern::SubjectPattern;
480
481 let mut subject_groups: AHashMap<String, Vec<TriplePattern>> = AHashMap::new();
483
484 for pattern in patterns {
485 let subject_key = match &pattern.subject {
486 Some(SubjectPattern::Variable(v)) => format!("var:{}", v.as_str()),
487 Some(SubjectPattern::NamedNode(n)) => format!("node:{}", n.as_str()),
488 Some(SubjectPattern::BlankNode(b)) => format!("blank:{}", b.as_str()),
489 None => continue,
490 };
491
492 subject_groups
493 .entry(subject_key)
494 .or_default()
495 .push(pattern.clone());
496 }
497
498 subject_groups
500 .into_values()
501 .max_by_key(|group| group.len())
502 .filter(|group| group.len() >= 2)
503 }
504
505 fn detect_chain_pattern(&self, patterns: &[TriplePattern]) -> Option<Vec<ChainLink>> {
507 if patterns.len() < 2 {
508 return None;
509 }
510
511 use crate::model::pattern::{ObjectPattern, SubjectPattern};
512
513 let mut chain = Vec::new();
514 let mut used = vec![false; patterns.len()];
515 let mut current_idx = 0;
516
517 chain.push(ChainLink {
519 pattern: patterns[0].clone(),
520 link_variable: None,
521 });
522 used[0] = true;
523
524 while current_idx < patterns.len() {
526 let current_object = match &patterns[current_idx].object {
527 Some(ObjectPattern::Variable(v)) => Some(v.clone()),
528 _ => None,
529 };
530
531 if let Some(obj_var) = current_object {
532 if let Some((next_idx, link_var)) =
534 patterns.iter().enumerate().find_map(|(idx, p)| {
535 if used[idx] {
536 return None;
537 }
538 match &p.subject {
539 Some(SubjectPattern::Variable(v)) if v == &obj_var => {
540 Some((idx, obj_var.clone()))
541 }
542 _ => None,
543 }
544 })
545 {
546 chain.push(ChainLink {
547 pattern: patterns[next_idx].clone(),
548 link_variable: Some(link_var),
549 });
550 used[next_idx] = true;
551 current_idx = next_idx;
552 continue;
553 }
554 }
555 break;
556 }
557
558 (chain.len() >= 2).then_some(chain)
560 }
561
562 fn detect_path_pattern(&self, _plan: &ExecutionPlan) -> Option<PathInfo> {
564 None
566 }
567
568 fn calculate_selectivity(&self, plan: &ExecutionPlan) -> Option<f64> {
570 if let Ok(estimates) = self.cardinality_estimates.read() {
571 let pattern_key = format!("{:?}", plan);
572 if let Some(estimate) = estimates.get(&pattern_key) {
573 let max_count = 1_000_000; let selectivity = 1.0 - (estimate.estimated_count as f64 / max_count as f64);
577 return Some(selectivity.clamp(0.0, 1.0));
578 }
579 }
580 None
581 }
582
583 fn optimize_selective_pattern(
585 &self,
586 plan: &ExecutionPlan,
587 _selectivity: f64,
588 ) -> Result<ExecutionPlan, OxirsError> {
589 Ok(plan.clone())
592 }
593
594 fn optimize_complex_pattern(&self, plan: &ExecutionPlan) -> Result<ExecutionPlan, OxirsError> {
596 Ok(plan.clone())
599 }
600
601 fn update_cardinality_estimates(&self, _plan: &ExecutionPlan, result: &QueryOutput) {
603 if let Ok(mut estimates) = self.cardinality_estimates.write() {
604 let pattern_key = format!("{:?}", _plan);
607 estimates.insert(
608 pattern_key,
609 CardinalityEstimate {
610 estimated_count: result.bindings.len(),
611 confidence: 0.8, last_updated: Instant::now(),
613 },
614 );
615 }
616 }
617
618 fn hash_plan(&self, plan: &ExecutionPlan) -> QueryHash {
620 use std::collections::hash_map::DefaultHasher;
621 use std::hash::{Hash, Hasher};
622
623 let mut hasher = DefaultHasher::new();
624 format!("{plan:?}").hash(&mut hasher);
625 hasher.finish()
626 }
627
628 fn get_compiled(&self, hash: QueryHash) -> Option<QueryFunction> {
630 let cache = self.compiled_cache.read().ok()?;
631 cache.queries.get(&hash).map(|q| {
632 q.function.clone()
634 })
635 }
636
637 fn execute_interpreted(
639 &self,
640 plan: &ExecutionPlan,
641 context: &QueryContext,
642 ) -> Result<QueryOutput, OxirsError> {
643 match plan {
644 ExecutionPlan::TripleScan { pattern } => self.execute_triple_scan(pattern, context),
645 ExecutionPlan::HashJoin {
646 left,
647 right,
648 join_vars,
649 } => self.execute_hash_join(left, right, join_vars, context),
650 _ => Err(OxirsError::Query("Plan type not supported".to_string())),
651 }
652 }
653
654 fn execute_triple_scan(
656 &self,
657 pattern: &TriplePattern,
658 context: &QueryContext,
659 ) -> Result<QueryOutput, OxirsError> {
660 let mut results = Vec::new();
661 let mut stats = QueryStats {
662 triples_scanned: 0,
663 results_count: 0,
664 execution_time: Duration::ZERO,
665 memory_used: 0,
666 };
667
668 let start = Instant::now();
669
670 for triple in context.data.triples.iter() {
672 stats.triples_scanned += 1;
673
674 if let Some(bindings) = self.match_triple(triple, pattern, &context.bindings) {
675 results.push(bindings);
676 stats.results_count += 1;
677
678 if results.len() >= context.limits.max_results {
679 break;
680 }
681 }
682 }
683
684 stats.execution_time = start.elapsed();
685 stats.memory_used = results.len() * std::mem::size_of::<HashMap<Variable, Term>>();
686
687 Ok(QueryOutput {
688 bindings: results,
689 stats,
690 })
691 }
692
693 fn match_triple(
695 &self,
696 triple: &Triple,
697 pattern: &crate::model::pattern::TriplePattern,
698 existing: &HashMap<Variable, Term>,
699 ) -> Option<HashMap<Variable, Term>> {
700 let mut bindings = existing.clone();
701
702 if let Some(ref subject_pattern) = pattern.subject {
704 if !self.match_subject_pattern(triple.subject(), subject_pattern, &mut bindings) {
705 return None;
706 }
707 }
708
709 if let Some(ref predicate_pattern) = pattern.predicate {
711 if !self.match_predicate_pattern(triple.predicate(), predicate_pattern, &mut bindings) {
712 return None;
713 }
714 }
715
716 if let Some(ref object_pattern) = pattern.object {
718 if !self.match_object_pattern(triple.object(), object_pattern, &mut bindings) {
719 return None;
720 }
721 }
722
723 Some(bindings)
724 }
725
726 fn match_term(
728 &self,
729 term: &Term,
730 pattern: &TermPattern,
731 bindings: &mut HashMap<Variable, Term>,
732 ) -> bool {
733 match pattern {
734 TermPattern::Variable(var) => {
735 if let Some(bound) = bindings.get(var) {
736 bound == term
737 } else {
738 bindings.insert(var.clone(), term.clone());
739 true
740 }
741 }
742 TermPattern::NamedNode(n) => {
743 matches!(term, Term::NamedNode(nn) if nn == n)
744 }
745 TermPattern::Literal(l) => {
746 matches!(term, Term::Literal(lit) if lit == l)
747 }
748 TermPattern::BlankNode(b) => {
749 matches!(term, Term::BlankNode(bn) if bn == b)
750 }
751 TermPattern::QuotedTriple(_) => {
752 panic!("RDF-star quoted triples not yet supported in JIT compilation")
753 }
754 }
755 }
756
757 fn match_subject_pattern(
759 &self,
760 subject: &Subject,
761 pattern: &crate::model::pattern::SubjectPattern,
762 bindings: &mut HashMap<Variable, Term>,
763 ) -> bool {
764 use crate::model::pattern::SubjectPattern;
765 match pattern {
766 SubjectPattern::Variable(var) => {
767 let term = Term::from_subject(subject);
768 if let Some(bound_value) = bindings.get(var) {
769 bound_value == &term
770 } else {
771 bindings.insert(var.clone(), term);
772 true
773 }
774 }
775 SubjectPattern::NamedNode(n) => matches!(subject, Subject::NamedNode(nn) if nn == n),
776 SubjectPattern::BlankNode(b) => matches!(subject, Subject::BlankNode(bn) if bn == b),
777 }
778 }
779
780 fn match_predicate_pattern(
782 &self,
783 predicate: &Predicate,
784 pattern: &crate::model::pattern::PredicatePattern,
785 bindings: &mut HashMap<Variable, Term>,
786 ) -> bool {
787 use crate::model::pattern::PredicatePattern;
788 match pattern {
789 PredicatePattern::Variable(var) => {
790 let term = Term::from_predicate(predicate);
791 if let Some(bound_value) = bindings.get(var) {
792 bound_value == &term
793 } else {
794 bindings.insert(var.clone(), term);
795 true
796 }
797 }
798 PredicatePattern::NamedNode(n) => {
799 matches!(predicate, Predicate::NamedNode(nn) if nn == n)
800 }
801 }
802 }
803
804 fn match_object_pattern(
806 &self,
807 object: &Object,
808 pattern: &crate::model::pattern::ObjectPattern,
809 bindings: &mut HashMap<Variable, Term>,
810 ) -> bool {
811 use crate::model::pattern::ObjectPattern;
812 match pattern {
813 ObjectPattern::Variable(var) => {
814 let term = Term::from_object(object);
815 if let Some(bound_value) = bindings.get(var) {
816 bound_value == &term
817 } else {
818 bindings.insert(var.clone(), term);
819 true
820 }
821 }
822 ObjectPattern::NamedNode(n) => matches!(object, Object::NamedNode(nn) if nn == n),
823 ObjectPattern::BlankNode(b) => matches!(object, Object::BlankNode(bn) if bn == b),
824 ObjectPattern::Literal(l) => matches!(object, Object::Literal(lit) if lit == l),
825 }
826 }
827
828 fn execute_hash_join(
830 &self,
831 left: &ExecutionPlan,
832 right: &ExecutionPlan,
833 join_vars: &[Variable],
834 context: &QueryContext,
835 ) -> Result<QueryOutput, OxirsError> {
836 let left_output = self.execute_interpreted(left, context)?;
838
839 let mut hash_table: HashMap<Vec<Term>, Vec<HashMap<Variable, Term>>> = HashMap::new();
841
842 for binding in left_output.bindings {
843 let key: Vec<Term> = join_vars
844 .iter()
845 .filter_map(|var| binding.get(var).cloned())
846 .collect();
847 hash_table.entry(key).or_default().push(binding);
848 }
849
850 let right_output = self.execute_interpreted(right, context)?;
852 let mut results = Vec::new();
853
854 for right_binding in right_output.bindings {
855 let key: Vec<Term> = join_vars
856 .iter()
857 .filter_map(|var| right_binding.get(var).cloned())
858 .collect();
859
860 if let Some(left_bindings) = hash_table.get(&key) {
861 for left_binding in left_bindings {
862 let mut merged = left_binding.clone();
863 merged.extend(right_binding.clone());
864 results.push(merged);
865 }
866 }
867 }
868
869 let results_count = results.len();
870 Ok(QueryOutput {
871 bindings: results,
872 stats: QueryStats {
873 triples_scanned: left_output.stats.triples_scanned
874 + right_output.stats.triples_scanned,
875 results_count,
876 execution_time: left_output.stats.execution_time
877 + right_output.stats.execution_time,
878 memory_used: left_output.stats.memory_used + right_output.stats.memory_used,
879 },
880 })
881 }
882
883 fn update_stats(&self, hash: QueryHash, execution_time: Duration) {
885 if let Ok(mut stats) = self.execution_stats.write() {
886 *stats.query_counts.entry(hash).or_insert(0) += 1;
887 stats
888 .query_times
889 .entry(hash)
890 .or_default()
891 .push(execution_time);
892 }
893 }
894
895 fn should_compile(&self, hash: QueryHash) -> bool {
897 if let Ok(stats) = self.execution_stats.read() {
898 if let Some(&count) = stats.query_counts.get(&hash) {
899 return count >= stats.hot_threshold;
900 }
901 }
902 false
903 }
904
905 fn compile_plan(&self, plan: &ExecutionPlan, hash: QueryHash) -> Result<(), OxirsError> {
907 let start = Instant::now();
908
909 let compiled = match plan {
911 ExecutionPlan::TripleScan { pattern } => self.compile_triple_scan(pattern)?,
912 ExecutionPlan::HashJoin {
913 left,
914 right,
915 join_vars,
916 } => self.compile_hash_join(left, right, join_vars)?,
917 _ => return Err(OxirsError::Query("Cannot compile plan type".to_string())),
918 };
919
920 let compile_time = start.elapsed();
921
922 if let Ok(mut cache) = self.compiled_cache.write() {
924 cache.add(
925 hash,
926 CompiledQuery {
927 function: compiled,
928 code_size: 1024, compile_time,
930 last_accessed: Instant::now(),
931 execution_count: 0,
932 },
933 );
934 }
935
936 Ok(())
937 }
938
939 fn compile_triple_scan(
941 &self,
942 pattern: &crate::model::pattern::TriplePattern,
943 ) -> Result<QueryFunction, OxirsError> {
944 let pattern = pattern.clone();
946
947 Ok(Arc::new(move |context: &QueryContext| {
948 let mut results = Vec::new();
949
950 if let Some(crate::model::pattern::PredicatePattern::NamedNode(pred)) =
952 &pattern.predicate
953 {
954 if let Some(indices) = context.data.indexes.by_predicate.get(&pred.clone().into()) {
956 for &idx in indices {
957 let triple = &context.data.triples[idx];
958 if let Some(bindings) =
960 match_triple_fast(triple, &pattern, &context.bindings)
961 {
962 results.push(bindings);
963 }
964 }
965 }
966 } else {
967 for triple in &context.data.triples {
969 if let Some(bindings) = match_triple_fast(triple, &pattern, &context.bindings) {
970 results.push(bindings);
971 }
972 }
973 }
974
975 let results_count = results.len();
976 Ok(QueryOutput {
977 bindings: results,
978 stats: QueryStats {
979 triples_scanned: context.data.triples.len(),
980 results_count,
981 execution_time: Duration::ZERO,
982 memory_used: 0,
983 },
984 })
985 }))
986 }
987
988 fn compile_hash_join(
990 &self,
991 _left: &ExecutionPlan,
992 _right: &ExecutionPlan,
993 _join_vars: &[Variable],
994 ) -> Result<QueryFunction, OxirsError> {
995 Ok(Arc::new(move |_context: &QueryContext| {
997 Ok(QueryOutput {
998 bindings: Vec::new(),
999 stats: QueryStats {
1000 triples_scanned: 0,
1001 results_count: 0,
1002 execution_time: Duration::ZERO,
1003 memory_used: 0,
1004 },
1005 })
1006 }))
1007 }
1008}
1009
1010fn match_triple_fast(
1012 triple: &Triple,
1013 pattern: &crate::model::pattern::TriplePattern,
1014 bindings: &HashMap<Variable, Term>,
1015) -> Option<HashMap<Variable, Term>> {
1016 let mut result = bindings.clone();
1017
1018 if let Some(ref subject_pattern) = pattern.subject {
1020 use crate::model::pattern::SubjectPattern;
1021 match subject_pattern {
1022 SubjectPattern::Variable(v) => {
1023 if let Some(bound) = bindings.get(v) {
1024 if bound != &Term::from_subject(triple.subject()) {
1025 return None;
1026 }
1027 } else {
1028 result.insert(v.clone(), Term::from_subject(triple.subject()));
1029 }
1030 }
1031 SubjectPattern::NamedNode(n) => {
1032 if let Subject::NamedNode(nn) = triple.subject() {
1033 if nn != n {
1034 return None;
1035 }
1036 } else {
1037 return None;
1038 }
1039 }
1040 SubjectPattern::BlankNode(b) => {
1041 if let Subject::BlankNode(bn) = triple.subject() {
1042 if bn != b {
1043 return None;
1044 }
1045 } else {
1046 return None;
1047 }
1048 }
1049 }
1050 }
1051
1052 Some(result)
1055}
1056
1057impl CompiledQueryCache {
1058 fn new() -> Self {
1059 Self {
1060 queries: HashMap::new(),
1061 total_size: 0,
1062 access_order: Vec::new(),
1063 }
1064 }
1065
1066 fn add(&mut self, hash: QueryHash, query: CompiledQuery) {
1067 self.total_size += query.code_size;
1068 self.queries.insert(hash, query);
1069 self.access_order.push(hash);
1070
1071 while self.total_size > 100 * 1024 * 1024 {
1073 if let Some(oldest) = self.access_order.first() {
1075 if let Some(removed) = self.queries.remove(oldest) {
1076 self.total_size -= removed.code_size;
1077 }
1078 self.access_order.remove(0);
1079 } else {
1080 break;
1081 }
1082 }
1083 }
1084}
1085
1086impl ExecutionStatistics {
1087 fn new(hot_threshold: usize) -> Self {
1088 Self {
1089 query_counts: HashMap::new(),
1090 query_times: HashMap::new(),
1091 hot_threshold,
1092 }
1093 }
1094}
1095
1096pub mod codegen {
1098 use super::*;
1099
1100 pub struct LlvmCodeGen {
1102 target: TargetConfig,
1104 }
1105
1106 pub struct TargetConfig {
1108 pub arch: String,
1110 pub features: String,
1112 pub opt_level: OptLevel,
1114 }
1115
1116 pub enum OptLevel {
1118 None,
1119 Less,
1120 Default,
1121 Aggressive,
1122 }
1123
1124 impl LlvmCodeGen {
1125 pub fn gen_triple_scan(&self, _pattern: &TriplePattern) -> Vec<u8> {
1127 vec![0x90] }
1130
1131 pub fn gen_vector_compare(&self) -> Vec<u8> {
1133 vec![0x90] }
1136 }
1137}
1138
1139#[derive(Debug, Clone)]
1141enum QueryPattern {
1142 StarPattern(Vec<TriplePattern>),
1144 ChainPattern(Vec<ChainLink>),
1146 PathPattern(PathInfo),
1148 SelectivePattern(f64),
1150 Complex,
1152 Simple,
1154}
1155
1156#[derive(Debug, Clone)]
1158struct ChainLink {
1159 pattern: TriplePattern,
1160 link_variable: Option<Variable>, }
1162
1163#[derive(Debug, Clone)]
1165struct PathInfo {
1166 start: Variable,
1167 end: Variable,
1168 property: Variable,
1169 min_length: usize,
1170 max_length: Option<usize>,
1171}
1172
1173impl StarPatternOptimizer {
1175 fn optimize(
1177 &self,
1178 plan: &ExecutionPlan,
1179 _patterns: &[TriplePattern],
1180 ) -> Result<ExecutionPlan, OxirsError> {
1181 Ok(plan.clone())
1185 }
1186}
1187
1188impl ChainPatternOptimizer {
1189 fn optimize(
1191 &self,
1192 plan: &ExecutionPlan,
1193 _chain: &[ChainLink],
1194 ) -> Result<ExecutionPlan, OxirsError> {
1195 Ok(plan.clone())
1198 }
1199}
1200
1201impl PathPatternOptimizer {
1202 fn optimize(
1204 &self,
1205 plan: &ExecutionPlan,
1206 _path_info: &PathInfo,
1207 ) -> Result<ExecutionPlan, OxirsError> {
1208 Ok(plan.clone())
1211 }
1212}
1213
1214#[cfg(test)]
1215mod tests {
1216 use super::*;
1217
1218 #[test]
1219 fn test_jit_compiler_creation() {
1220 let compiler = JitCompiler::new();
1221
1222 let stats = compiler
1223 .execution_stats
1224 .read()
1225 .expect("lock should not be poisoned");
1226 assert_eq!(stats.query_counts.len(), 0);
1227 }
1228
1229 #[test]
1230 fn test_query_hashing() {
1231 let compiler = JitCompiler::new();
1232
1233 let plan = ExecutionPlan::TripleScan {
1234 pattern: crate::model::pattern::TriplePattern::new(
1235 Some(crate::model::pattern::SubjectPattern::Variable(
1236 Variable::new("?s").expect("valid variable name"),
1237 )),
1238 Some(crate::model::pattern::PredicatePattern::Variable(
1239 Variable::new("?p").expect("valid variable name"),
1240 )),
1241 Some(crate::model::pattern::ObjectPattern::Variable(
1242 Variable::new("?o").expect("valid variable name"),
1243 )),
1244 ),
1245 };
1246
1247 let hash1 = compiler.hash_plan(&plan);
1248 let hash2 = compiler.hash_plan(&plan);
1249
1250 assert_eq!(hash1, hash2);
1251 }
1252}