#![allow(dead_code)]
use crate::model::pattern::TriplePattern;
use crate::model::{Object, Predicate, Subject, Term, Triple, Variable};
use crate::query::algebra::TermPattern;
use crate::query::plan::ExecutionPlan;
use crate::OxirsError;
use ahash::AHashMap;
use lru::LruCache;
use std::collections::HashMap;
use std::num::NonZeroUsize;
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};
pub struct JitCompiler {
compiled_cache: Arc<RwLock<CompiledQueryCache>>,
execution_stats: Arc<RwLock<ExecutionStatistics>>,
result_cache: Arc<Mutex<LruCache<QueryHash, CachedQueryResult>>>,
cardinality_estimates: Arc<RwLock<AHashMap<String, CardinalityEstimate>>>,
pattern_optimizers: Arc<PatternOptimizers>,
config: JitConfig,
}
#[derive(Clone)]
struct CachedQueryResult {
output: QueryOutput,
cached_at: Instant,
ttl: Duration,
}
#[derive(Debug, Clone)]
struct CardinalityEstimate {
estimated_count: usize,
confidence: f64, last_updated: Instant,
}
struct PatternOptimizers {
star_optimizer: StarPatternOptimizer,
chain_optimizer: ChainPatternOptimizer,
path_optimizer: PathPatternOptimizer,
}
struct StarPatternOptimizer;
struct ChainPatternOptimizer;
struct PathPatternOptimizer;
#[derive(Debug, Clone)]
pub struct JitConfig {
pub compilation_threshold: usize,
pub max_cache_size: usize,
pub aggressive_opts: bool,
pub target_features: TargetFeatures,
pub enable_result_cache: bool,
pub result_cache_size: usize,
pub result_cache_ttl: Duration,
pub enable_adaptive_optimization: bool,
pub enable_pattern_optimizers: bool,
}
impl Default for JitConfig {
fn default() -> Self {
Self {
compilation_threshold: 10,
max_cache_size: 100 * 1024 * 1024, aggressive_opts: true,
target_features: TargetFeatures::default(),
enable_result_cache: true,
result_cache_size: 1000,
result_cache_ttl: Duration::from_secs(300), enable_adaptive_optimization: true,
enable_pattern_optimizers: true,
}
}
}
#[derive(Debug, Clone)]
pub struct TargetFeatures {
pub avx2: bool,
pub avx512: bool,
pub bmi2: bool,
pub vectorize: bool,
}
impl Default for TargetFeatures {
fn default() -> Self {
Self {
avx2: cfg!(target_feature = "avx2"),
avx512: cfg!(target_feature = "avx512f"),
bmi2: cfg!(target_feature = "bmi2"),
vectorize: true,
}
}
}
struct CompiledQueryCache {
queries: HashMap<QueryHash, CompiledQuery>,
total_size: usize,
access_order: Vec<QueryHash>,
}
struct CompiledQuery {
function: QueryFunction,
code_size: usize,
compile_time: Duration,
last_accessed: Instant,
execution_count: usize,
}
type QueryHash = u64;
type QueryFunction = Arc<dyn Fn(&QueryContext) -> Result<QueryOutput, OxirsError> + Send + Sync>;
pub struct QueryContext {
pub data: Arc<GraphData>,
pub bindings: HashMap<Variable, Term>,
pub limits: ExecutionLimits,
}
pub struct GraphData {
pub triples: Vec<Triple>,
pub indexes: QueryIndexes,
}
pub struct QueryIndexes {
pub by_subject: HashMap<Subject, Vec<usize>>,
pub by_predicate: HashMap<Predicate, Vec<usize>>,
pub by_object: HashMap<Object, Vec<usize>>,
}
#[derive(Debug, Clone)]
pub struct ExecutionLimits {
pub max_results: usize,
pub timeout: Duration,
pub memory_limit: usize,
}
#[derive(Clone)]
pub struct QueryOutput {
pub bindings: Vec<HashMap<Variable, Term>>,
pub stats: QueryStats,
}
#[derive(Debug, Clone)]
pub struct QueryStats {
pub triples_scanned: usize,
pub results_count: usize,
pub execution_time: Duration,
pub memory_used: usize,
}
struct ExecutionStatistics {
query_counts: HashMap<QueryHash, usize>,
query_times: HashMap<QueryHash, Vec<Duration>>,
hot_threshold: usize,
}
impl Default for JitCompiler {
fn default() -> Self {
Self::with_config(JitConfig::default())
}
}
impl JitCompiler {
pub fn new() -> Self {
Self::default()
}
pub fn with_config(config: JitConfig) -> Self {
let cache_size = NonZeroUsize::new(config.result_cache_size)
.unwrap_or(NonZeroUsize::new(1000).expect("constant is non-zero"));
Self {
compiled_cache: Arc::new(RwLock::new(CompiledQueryCache::new())),
execution_stats: Arc::new(RwLock::new(ExecutionStatistics::new(
config.compilation_threshold,
))),
result_cache: Arc::new(Mutex::new(LruCache::new(cache_size))),
cardinality_estimates: Arc::new(RwLock::new(AHashMap::new())),
pattern_optimizers: Arc::new(PatternOptimizers {
star_optimizer: StarPatternOptimizer,
chain_optimizer: ChainPatternOptimizer,
path_optimizer: PathPatternOptimizer,
}),
config,
}
}
pub fn execute(
&self,
plan: &ExecutionPlan,
context: QueryContext,
) -> Result<QueryOutput, OxirsError> {
let hash = self.hash_plan(plan);
if self.config.enable_result_cache {
if let Some(cached) = self.get_cached_result(hash) {
return Ok(cached.output.clone());
}
}
if let Some(compiled) = self.get_compiled(hash) {
let result = (compiled)(&context)?;
if self.config.enable_result_cache {
self.cache_result(hash, result.clone());
}
return Ok(result);
}
let optimized_plan = if self.config.enable_pattern_optimizers {
self.optimize_pattern(plan)?
} else {
plan.clone()
};
let start = Instant::now();
let result = self.execute_interpreted(&optimized_plan, &context)?;
let execution_time = start.elapsed();
self.update_stats(hash, execution_time);
if self.config.enable_adaptive_optimization {
self.update_cardinality_estimates(plan, &result);
}
if self.should_compile(hash) {
self.compile_plan(&optimized_plan, hash)?;
}
if self.config.enable_result_cache {
self.cache_result(hash, result.clone());
}
Ok(result)
}
fn get_cached_result(&self, hash: QueryHash) -> Option<CachedQueryResult> {
let mut cache = self.result_cache.lock().ok()?;
let cached = cache.get(&hash)?;
if cached.cached_at.elapsed() > cached.ttl {
cache.pop(&hash);
return None;
}
Some(cached.clone())
}
fn cache_result(&self, hash: QueryHash, output: QueryOutput) {
if let Ok(mut cache) = self.result_cache.lock() {
cache.put(
hash,
CachedQueryResult {
output,
cached_at: Instant::now(),
ttl: self.config.result_cache_ttl,
},
);
}
}
fn optimize_pattern(&self, plan: &ExecutionPlan) -> Result<ExecutionPlan, OxirsError> {
if !self.config.enable_pattern_optimizers {
return Ok(plan.clone());
}
match self.detect_query_pattern(plan) {
QueryPattern::StarPattern(patterns) => {
tracing::debug!("Detected star pattern with {} arms", patterns.len());
self.pattern_optimizers
.star_optimizer
.optimize(plan, &patterns)
}
QueryPattern::ChainPattern(chain_info) => {
tracing::debug!("Detected chain pattern with {} links", chain_info.len());
self.pattern_optimizers
.chain_optimizer
.optimize(plan, &chain_info)
}
QueryPattern::PathPattern(path_info) => {
tracing::debug!("Detected path pattern: {:?}", path_info);
self.pattern_optimizers
.path_optimizer
.optimize(plan, &path_info)
}
QueryPattern::SelectivePattern(selectivity) => {
tracing::debug!(
"Detected selective pattern (selectivity: {:.2})",
selectivity
);
self.optimize_selective_pattern(plan, selectivity)
}
QueryPattern::Complex => {
tracing::debug!("Complex pattern - applying general optimizations");
self.optimize_complex_pattern(plan)
}
QueryPattern::Simple => {
Ok(plan.clone())
}
}
}
fn detect_query_pattern(&self, plan: &ExecutionPlan) -> QueryPattern {
let patterns = self.extract_triple_patterns(plan);
if let Some(star_patterns) = self.detect_star_pattern(&patterns) {
return QueryPattern::StarPattern(star_patterns);
}
if let Some(chain) = self.detect_chain_pattern(&patterns) {
return QueryPattern::ChainPattern(chain);
}
if let Some(path_info) = self.detect_path_pattern(plan) {
return QueryPattern::PathPattern(path_info);
}
if let Some(selectivity) = self.calculate_selectivity(plan) {
if selectivity > 0.8 {
return QueryPattern::SelectivePattern(selectivity);
}
}
if patterns.len() > 3 {
QueryPattern::Complex
} else {
QueryPattern::Simple
}
}
fn extract_triple_patterns(&self, plan: &ExecutionPlan) -> Vec<TriplePattern> {
let _self = self;
let mut patterns = Vec::new();
match plan {
ExecutionPlan::TripleScan { pattern } => {
patterns.push(pattern.clone());
}
ExecutionPlan::HashJoin { left, right, .. } => {
patterns.extend(self.extract_triple_patterns(left));
patterns.extend(self.extract_triple_patterns(right));
}
ExecutionPlan::Union { left, right } => {
patterns.extend(self.extract_triple_patterns(left));
patterns.extend(self.extract_triple_patterns(right));
}
ExecutionPlan::Filter { input, .. }
| ExecutionPlan::Project { input, .. }
| ExecutionPlan::Distinct { input, .. }
| ExecutionPlan::Sort { input, .. }
| ExecutionPlan::Limit { input, .. } => {
patterns.extend(self.extract_triple_patterns(input));
}
}
patterns
}
fn detect_star_pattern(&self, patterns: &[TriplePattern]) -> Option<Vec<TriplePattern>> {
if patterns.len() < 2 {
return None;
}
use crate::model::pattern::SubjectPattern;
let mut subject_groups: AHashMap<String, Vec<TriplePattern>> = AHashMap::new();
for pattern in patterns {
let subject_key = match &pattern.subject {
Some(SubjectPattern::Variable(v)) => format!("var:{}", v.as_str()),
Some(SubjectPattern::NamedNode(n)) => format!("node:{}", n.as_str()),
Some(SubjectPattern::BlankNode(b)) => format!("blank:{}", b.as_str()),
None => continue,
};
subject_groups
.entry(subject_key)
.or_default()
.push(pattern.clone());
}
subject_groups
.into_values()
.max_by_key(|group| group.len())
.filter(|group| group.len() >= 2)
}
fn detect_chain_pattern(&self, patterns: &[TriplePattern]) -> Option<Vec<ChainLink>> {
if patterns.len() < 2 {
return None;
}
use crate::model::pattern::{ObjectPattern, SubjectPattern};
let mut chain = Vec::new();
let mut used = vec![false; patterns.len()];
let mut current_idx = 0;
chain.push(ChainLink {
pattern: patterns[0].clone(),
link_variable: None,
});
used[0] = true;
while current_idx < patterns.len() {
let current_object = match &patterns[current_idx].object {
Some(ObjectPattern::Variable(v)) => Some(v.clone()),
_ => None,
};
if let Some(obj_var) = current_object {
if let Some((next_idx, link_var)) =
patterns.iter().enumerate().find_map(|(idx, p)| {
if used[idx] {
return None;
}
match &p.subject {
Some(SubjectPattern::Variable(v)) if v == &obj_var => {
Some((idx, obj_var.clone()))
}
_ => None,
}
})
{
chain.push(ChainLink {
pattern: patterns[next_idx].clone(),
link_variable: Some(link_var),
});
used[next_idx] = true;
current_idx = next_idx;
continue;
}
}
break;
}
(chain.len() >= 2).then_some(chain)
}
fn detect_path_pattern(&self, _plan: &ExecutionPlan) -> Option<PathInfo> {
None
}
fn calculate_selectivity(&self, plan: &ExecutionPlan) -> Option<f64> {
if let Ok(estimates) = self.cardinality_estimates.read() {
let pattern_key = format!("{:?}", plan);
if let Some(estimate) = estimates.get(&pattern_key) {
let max_count = 1_000_000; let selectivity = 1.0 - (estimate.estimated_count as f64 / max_count as f64);
return Some(selectivity.clamp(0.0, 1.0));
}
}
None
}
fn optimize_selective_pattern(
&self,
plan: &ExecutionPlan,
_selectivity: f64,
) -> Result<ExecutionPlan, OxirsError> {
Ok(plan.clone())
}
fn optimize_complex_pattern(&self, plan: &ExecutionPlan) -> Result<ExecutionPlan, OxirsError> {
Ok(plan.clone())
}
fn update_cardinality_estimates(&self, _plan: &ExecutionPlan, result: &QueryOutput) {
if let Ok(mut estimates) = self.cardinality_estimates.write() {
let pattern_key = format!("{:?}", _plan);
estimates.insert(
pattern_key,
CardinalityEstimate {
estimated_count: result.bindings.len(),
confidence: 0.8, last_updated: Instant::now(),
},
);
}
}
fn hash_plan(&self, plan: &ExecutionPlan) -> QueryHash {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
format!("{plan:?}").hash(&mut hasher);
hasher.finish()
}
fn get_compiled(&self, hash: QueryHash) -> Option<QueryFunction> {
let cache = self.compiled_cache.read().ok()?;
cache.queries.get(&hash).map(|q| {
q.function.clone()
})
}
fn execute_interpreted(
&self,
plan: &ExecutionPlan,
context: &QueryContext,
) -> Result<QueryOutput, OxirsError> {
match plan {
ExecutionPlan::TripleScan { pattern } => self.execute_triple_scan(pattern, context),
ExecutionPlan::HashJoin {
left,
right,
join_vars,
} => self.execute_hash_join(left, right, join_vars, context),
_ => Err(OxirsError::Query("Plan type not supported".to_string())),
}
}
fn execute_triple_scan(
&self,
pattern: &TriplePattern,
context: &QueryContext,
) -> Result<QueryOutput, OxirsError> {
let mut results = Vec::new();
let mut stats = QueryStats {
triples_scanned: 0,
results_count: 0,
execution_time: Duration::ZERO,
memory_used: 0,
};
let start = Instant::now();
for triple in context.data.triples.iter() {
stats.triples_scanned += 1;
if let Some(bindings) = self.match_triple(triple, pattern, &context.bindings) {
results.push(bindings);
stats.results_count += 1;
if results.len() >= context.limits.max_results {
break;
}
}
}
stats.execution_time = start.elapsed();
stats.memory_used = results.len() * std::mem::size_of::<HashMap<Variable, Term>>();
Ok(QueryOutput {
bindings: results,
stats,
})
}
fn match_triple(
&self,
triple: &Triple,
pattern: &crate::model::pattern::TriplePattern,
existing: &HashMap<Variable, Term>,
) -> Option<HashMap<Variable, Term>> {
let mut bindings = existing.clone();
if let Some(ref subject_pattern) = pattern.subject {
if !self.match_subject_pattern(triple.subject(), subject_pattern, &mut bindings) {
return None;
}
}
if let Some(ref predicate_pattern) = pattern.predicate {
if !self.match_predicate_pattern(triple.predicate(), predicate_pattern, &mut bindings) {
return None;
}
}
if let Some(ref object_pattern) = pattern.object {
if !self.match_object_pattern(triple.object(), object_pattern, &mut bindings) {
return None;
}
}
Some(bindings)
}
fn match_term(
&self,
term: &Term,
pattern: &TermPattern,
bindings: &mut HashMap<Variable, Term>,
) -> bool {
match pattern {
TermPattern::Variable(var) => {
if let Some(bound) = bindings.get(var) {
bound == term
} else {
bindings.insert(var.clone(), term.clone());
true
}
}
TermPattern::NamedNode(n) => {
matches!(term, Term::NamedNode(nn) if nn == n)
}
TermPattern::Literal(l) => {
matches!(term, Term::Literal(lit) if lit == l)
}
TermPattern::BlankNode(b) => {
matches!(term, Term::BlankNode(bn) if bn == b)
}
TermPattern::QuotedTriple(_) => {
panic!("RDF-star quoted triples not yet supported in JIT compilation")
}
}
}
fn match_subject_pattern(
&self,
subject: &Subject,
pattern: &crate::model::pattern::SubjectPattern,
bindings: &mut HashMap<Variable, Term>,
) -> bool {
use crate::model::pattern::SubjectPattern;
match pattern {
SubjectPattern::Variable(var) => {
let term = Term::from_subject(subject);
if let Some(bound_value) = bindings.get(var) {
bound_value == &term
} else {
bindings.insert(var.clone(), term);
true
}
}
SubjectPattern::NamedNode(n) => matches!(subject, Subject::NamedNode(nn) if nn == n),
SubjectPattern::BlankNode(b) => matches!(subject, Subject::BlankNode(bn) if bn == b),
}
}
fn match_predicate_pattern(
&self,
predicate: &Predicate,
pattern: &crate::model::pattern::PredicatePattern,
bindings: &mut HashMap<Variable, Term>,
) -> bool {
use crate::model::pattern::PredicatePattern;
match pattern {
PredicatePattern::Variable(var) => {
let term = Term::from_predicate(predicate);
if let Some(bound_value) = bindings.get(var) {
bound_value == &term
} else {
bindings.insert(var.clone(), term);
true
}
}
PredicatePattern::NamedNode(n) => {
matches!(predicate, Predicate::NamedNode(nn) if nn == n)
}
}
}
fn match_object_pattern(
&self,
object: &Object,
pattern: &crate::model::pattern::ObjectPattern,
bindings: &mut HashMap<Variable, Term>,
) -> bool {
use crate::model::pattern::ObjectPattern;
match pattern {
ObjectPattern::Variable(var) => {
let term = Term::from_object(object);
if let Some(bound_value) = bindings.get(var) {
bound_value == &term
} else {
bindings.insert(var.clone(), term);
true
}
}
ObjectPattern::NamedNode(n) => matches!(object, Object::NamedNode(nn) if nn == n),
ObjectPattern::BlankNode(b) => matches!(object, Object::BlankNode(bn) if bn == b),
ObjectPattern::Literal(l) => matches!(object, Object::Literal(lit) if lit == l),
}
}
fn execute_hash_join(
&self,
left: &ExecutionPlan,
right: &ExecutionPlan,
join_vars: &[Variable],
context: &QueryContext,
) -> Result<QueryOutput, OxirsError> {
let left_output = self.execute_interpreted(left, context)?;
let mut hash_table: HashMap<Vec<Term>, Vec<HashMap<Variable, Term>>> = HashMap::new();
for binding in left_output.bindings {
let key: Vec<Term> = join_vars
.iter()
.filter_map(|var| binding.get(var).cloned())
.collect();
hash_table.entry(key).or_default().push(binding);
}
let right_output = self.execute_interpreted(right, context)?;
let mut results = Vec::new();
for right_binding in right_output.bindings {
let key: Vec<Term> = join_vars
.iter()
.filter_map(|var| right_binding.get(var).cloned())
.collect();
if let Some(left_bindings) = hash_table.get(&key) {
for left_binding in left_bindings {
let mut merged = left_binding.clone();
merged.extend(right_binding.clone());
results.push(merged);
}
}
}
let results_count = results.len();
Ok(QueryOutput {
bindings: results,
stats: QueryStats {
triples_scanned: left_output.stats.triples_scanned
+ right_output.stats.triples_scanned,
results_count,
execution_time: left_output.stats.execution_time
+ right_output.stats.execution_time,
memory_used: left_output.stats.memory_used + right_output.stats.memory_used,
},
})
}
fn update_stats(&self, hash: QueryHash, execution_time: Duration) {
if let Ok(mut stats) = self.execution_stats.write() {
*stats.query_counts.entry(hash).or_insert(0) += 1;
stats
.query_times
.entry(hash)
.or_default()
.push(execution_time);
}
}
fn should_compile(&self, hash: QueryHash) -> bool {
if let Ok(stats) = self.execution_stats.read() {
if let Some(&count) = stats.query_counts.get(&hash) {
return count >= stats.hot_threshold;
}
}
false
}
fn compile_plan(&self, plan: &ExecutionPlan, hash: QueryHash) -> Result<(), OxirsError> {
let start = Instant::now();
let compiled = match plan {
ExecutionPlan::TripleScan { pattern } => self.compile_triple_scan(pattern)?,
ExecutionPlan::HashJoin {
left,
right,
join_vars,
} => self.compile_hash_join(left, right, join_vars)?,
_ => return Err(OxirsError::Query("Cannot compile plan type".to_string())),
};
let compile_time = start.elapsed();
if let Ok(mut cache) = self.compiled_cache.write() {
cache.add(
hash,
CompiledQuery {
function: compiled,
code_size: 1024, compile_time,
last_accessed: Instant::now(),
execution_count: 0,
},
);
}
Ok(())
}
fn compile_triple_scan(
&self,
pattern: &crate::model::pattern::TriplePattern,
) -> Result<QueryFunction, OxirsError> {
let pattern = pattern.clone();
Ok(Arc::new(move |context: &QueryContext| {
let mut results = Vec::new();
if let Some(crate::model::pattern::PredicatePattern::NamedNode(pred)) =
&pattern.predicate
{
if let Some(indices) = context.data.indexes.by_predicate.get(&pred.clone().into()) {
for &idx in indices {
let triple = &context.data.triples[idx];
if let Some(bindings) =
match_triple_fast(triple, &pattern, &context.bindings)
{
results.push(bindings);
}
}
}
} else {
for triple in &context.data.triples {
if let Some(bindings) = match_triple_fast(triple, &pattern, &context.bindings) {
results.push(bindings);
}
}
}
let results_count = results.len();
Ok(QueryOutput {
bindings: results,
stats: QueryStats {
triples_scanned: context.data.triples.len(),
results_count,
execution_time: Duration::ZERO,
memory_used: 0,
},
})
}))
}
fn compile_hash_join(
&self,
_left: &ExecutionPlan,
_right: &ExecutionPlan,
_join_vars: &[Variable],
) -> Result<QueryFunction, OxirsError> {
Ok(Arc::new(move |_context: &QueryContext| {
Ok(QueryOutput {
bindings: Vec::new(),
stats: QueryStats {
triples_scanned: 0,
results_count: 0,
execution_time: Duration::ZERO,
memory_used: 0,
},
})
}))
}
}
fn match_triple_fast(
triple: &Triple,
pattern: &crate::model::pattern::TriplePattern,
bindings: &HashMap<Variable, Term>,
) -> Option<HashMap<Variable, Term>> {
let mut result = bindings.clone();
if let Some(ref subject_pattern) = pattern.subject {
use crate::model::pattern::SubjectPattern;
match subject_pattern {
SubjectPattern::Variable(v) => {
if let Some(bound) = bindings.get(v) {
if bound != &Term::from_subject(triple.subject()) {
return None;
}
} else {
result.insert(v.clone(), Term::from_subject(triple.subject()));
}
}
SubjectPattern::NamedNode(n) => {
if let Subject::NamedNode(nn) = triple.subject() {
if nn != n {
return None;
}
} else {
return None;
}
}
SubjectPattern::BlankNode(b) => {
if let Subject::BlankNode(bn) = triple.subject() {
if bn != b {
return None;
}
} else {
return None;
}
}
}
}
Some(result)
}
impl CompiledQueryCache {
fn new() -> Self {
Self {
queries: HashMap::new(),
total_size: 0,
access_order: Vec::new(),
}
}
fn add(&mut self, hash: QueryHash, query: CompiledQuery) {
self.total_size += query.code_size;
self.queries.insert(hash, query);
self.access_order.push(hash);
while self.total_size > 100 * 1024 * 1024 {
if let Some(oldest) = self.access_order.first() {
if let Some(removed) = self.queries.remove(oldest) {
self.total_size -= removed.code_size;
}
self.access_order.remove(0);
} else {
break;
}
}
}
}
impl ExecutionStatistics {
fn new(hot_threshold: usize) -> Self {
Self {
query_counts: HashMap::new(),
query_times: HashMap::new(),
hot_threshold,
}
}
}
pub mod codegen {
use super::*;
pub struct LlvmCodeGen {
target: TargetConfig,
}
pub struct TargetConfig {
pub arch: String,
pub features: String,
pub opt_level: OptLevel,
}
pub enum OptLevel {
None,
Less,
Default,
Aggressive,
}
impl LlvmCodeGen {
pub fn gen_triple_scan(&self, _pattern: &TriplePattern) -> Vec<u8> {
vec![0x90] }
pub fn gen_vector_compare(&self) -> Vec<u8> {
vec![0x90] }
}
}
#[derive(Debug, Clone)]
enum QueryPattern {
StarPattern(Vec<TriplePattern>),
ChainPattern(Vec<ChainLink>),
PathPattern(PathInfo),
SelectivePattern(f64),
Complex,
Simple,
}
#[derive(Debug, Clone)]
struct ChainLink {
pattern: TriplePattern,
link_variable: Option<Variable>, }
#[derive(Debug, Clone)]
struct PathInfo {
start: Variable,
end: Variable,
property: Variable,
min_length: usize,
max_length: Option<usize>,
}
impl StarPatternOptimizer {
fn optimize(
&self,
plan: &ExecutionPlan,
_patterns: &[TriplePattern],
) -> Result<ExecutionPlan, OxirsError> {
Ok(plan.clone())
}
}
impl ChainPatternOptimizer {
fn optimize(
&self,
plan: &ExecutionPlan,
_chain: &[ChainLink],
) -> Result<ExecutionPlan, OxirsError> {
Ok(plan.clone())
}
}
impl PathPatternOptimizer {
fn optimize(
&self,
plan: &ExecutionPlan,
_path_info: &PathInfo,
) -> Result<ExecutionPlan, OxirsError> {
Ok(plan.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_jit_compiler_creation() {
let compiler = JitCompiler::new();
let stats = compiler
.execution_stats
.read()
.expect("lock should not be poisoned");
assert_eq!(stats.query_counts.len(), 0);
}
#[test]
fn test_query_hashing() {
let compiler = JitCompiler::new();
let plan = ExecutionPlan::TripleScan {
pattern: crate::model::pattern::TriplePattern::new(
Some(crate::model::pattern::SubjectPattern::Variable(
Variable::new("?s").expect("valid variable name"),
)),
Some(crate::model::pattern::PredicatePattern::Variable(
Variable::new("?p").expect("valid variable name"),
)),
Some(crate::model::pattern::ObjectPattern::Variable(
Variable::new("?o").expect("valid variable name"),
)),
),
};
let hash1 = compiler.hash_plan(&plan);
let hash2 = compiler.hash_plan(&plan);
assert_eq!(hash1, hash2);
}
}