use super::*;
use crate::rdf_store::{OxirsQueryResults, StorageBackend};
use crate::{OxirsError, Result};
use scirs2_core::metrics::{Counter, Histogram, Timer};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct ExecutorStats {
pub total_queries: u64,
pub select_queries: u64,
pub ask_queries: u64,
pub construct_queries: u64,
pub describe_queries: u64,
pub pattern_matches: u64,
pub avg_execution_time_secs: f64,
pub total_observations: u64,
}
#[derive(Debug, Clone)]
pub(crate) struct ValuesClause {
pub(crate) variables: Vec<String>,
pub(crate) rows: Vec<Vec<String>>,
}
pub struct QueryExecutor<'a> {
pub(crate) backend: &'a StorageBackend,
pub(crate) query_timer: Arc<Timer>,
pub(crate) select_counter: Arc<Counter>,
pub(crate) ask_counter: Arc<Counter>,
pub(crate) construct_counter: Arc<Counter>,
pub(crate) describe_counter: Arc<Counter>,
pub(crate) pattern_counter: Arc<Counter>,
pub(crate) result_size_histogram: Arc<Histogram>,
}
impl<'a> QueryExecutor<'a> {
pub fn new(backend: &'a StorageBackend) -> Self {
Self {
backend,
query_timer: Arc::new(Timer::new("query_execution_time".to_string())),
select_counter: Arc::new(Counter::new("select_queries".to_string())),
ask_counter: Arc::new(Counter::new("ask_queries".to_string())),
construct_counter: Arc::new(Counter::new("construct_queries".to_string())),
describe_counter: Arc::new(Counter::new("describe_queries".to_string())),
pattern_counter: Arc::new(Counter::new("pattern_matches".to_string())),
result_size_histogram: Arc::new(Histogram::new("result_set_size".to_string())),
}
}
pub fn get_stats(&self) -> ExecutorStats {
let select = self.select_counter.get();
let ask = self.ask_counter.get();
let construct = self.construct_counter.get();
let describe = self.describe_counter.get();
let timer_stats = self.query_timer.get_stats();
ExecutorStats {
total_queries: select + ask + construct + describe,
select_queries: select,
ask_queries: ask,
construct_queries: construct,
describe_queries: describe,
pattern_matches: self.pattern_counter.get(),
avg_execution_time_secs: timer_stats.mean,
total_observations: timer_stats.count,
}
}
pub fn execute(&self, sparql: &str) -> Result<OxirsQueryResults> {
self.query(sparql)
}
pub fn query(&self, sparql: &str) -> Result<OxirsQueryResults> {
let start = std::time::Instant::now();
let sparql = sparql.trim();
let (prefixes, expanded_query) = self.extract_and_expand_prefixes(sparql)?;
let query_to_execute = if !prefixes.is_empty() {
&expanded_query
} else {
sparql
};
let result = if query_to_execute.to_uppercase().contains("SELECT") {
self.select_counter.inc();
self.execute_select_query(query_to_execute)
} else if query_to_execute.to_uppercase().starts_with("ASK") {
self.ask_counter.inc();
self.execute_ask_query(query_to_execute)
} else if query_to_execute.to_uppercase().starts_with("CONSTRUCT") {
self.construct_counter.inc();
self.execute_construct_query(query_to_execute)
} else if query_to_execute.to_uppercase().starts_with("DESCRIBE") {
self.describe_counter.inc();
self.execute_describe_query(query_to_execute)
} else {
return Err(OxirsError::Query(format!(
"Unsupported SPARQL query type: {sparql}"
)));
};
let duration = start.elapsed();
self.query_timer.observe(duration);
if let Ok(ref query_result) = result {
self.result_size_histogram
.observe(query_result.len() as f64);
}
result
}
fn extract_and_expand_prefixes(
&self,
sparql: &str,
) -> Result<(HashMap<String, String>, String)> {
extract_and_expand_prefixes(sparql)
}
}