mod algebra;
mod dataset;
mod error;
#[cfg(feature = "http-client")]
mod http;
pub mod results;
mod update;
use crate::model::{NamedNode, Term};
#[expect(deprecated)]
pub use crate::sparql::algebra::{Query, Update};
use crate::sparql::dataset::DatasetView;
pub use crate::sparql::error::UpdateEvaluationError;
#[cfg(feature = "http-client")]
use crate::sparql::http::HttpServiceHandler;
pub use crate::sparql::update::{BoundPreparedSparqlUpdate, PreparedSparqlUpdate};
use crate::store::{Store, Transaction};
use oxrdf::IriParseError;
pub use oxrdf::{Variable, VariableNameParseError};
pub use spareval::{
AggregateFunctionAccumulator, CancellationToken, DefaultServiceHandler,
QueryDatasetSpecification, QueryEvaluationError, QueryExplanation, QueryResults, QuerySolution,
QuerySolutionIter, QueryTripleIter, ServiceHandler,
};
use spareval::{QueryEvaluator, QueryableDataset};
use spargebra::SparqlParser;
pub use spargebra::SparqlSyntaxError;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::mem::take;
#[cfg(feature = "http-client")]
use std::time::Duration;
#[deprecated(note = "Use SparqlEvaluator instead", since = "0.5.0")]
pub type QueryOptions = SparqlEvaluator;
#[deprecated(note = "Use SparqlEvaluator instead", since = "0.5.0")]
pub type UpdateOptions = SparqlEvaluator;
#[deprecated(note = "Use QueryEvaluationError instead", since = "0.5.0")]
pub type EvaluationError = QueryEvaluationError;
pub type QueryDataset = QueryDatasetSpecification;
#[derive(Clone)]
#[must_use]
pub struct SparqlEvaluator {
#[cfg(feature = "http-client")]
http_timeout: Option<Duration>,
#[cfg(feature = "http-client")]
http_redirection_limit: usize,
#[cfg(feature = "http-client")]
with_http_default_service_handler: bool,
parser: SparqlParser,
inner: QueryEvaluator,
}
impl SparqlEvaluator {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn with_base_iri(mut self, base_iri: impl Into<String>) -> Result<Self, IriParseError> {
self.parser = self.parser.with_base_iri(base_iri)?;
Ok(self)
}
#[inline]
pub fn with_prefix(
mut self,
prefix_name: impl Into<String>,
prefix_iri: impl Into<String>,
) -> Result<Self, IriParseError> {
self.parser = self.parser.with_prefix(prefix_name, prefix_iri)?;
Ok(self)
}
#[inline]
pub fn with_service_handler(
mut self,
service_name: impl Into<NamedNode>,
handler: impl ServiceHandler + 'static,
) -> Self {
self.inner = self.inner.with_service_handler(service_name, handler);
self
}
#[inline]
pub fn with_default_service_handler(
mut self,
handler: impl DefaultServiceHandler + 'static,
) -> Self {
#[cfg(feature = "http-client")]
{
self.with_http_default_service_handler = false;
}
self.inner = self.inner.with_default_service_handler(handler);
self
}
#[cfg(feature = "http-client")]
#[inline]
pub fn without_default_http_service_handler(mut self) -> Self {
self.with_http_default_service_handler = false;
self
}
#[cfg(feature = "http-client")]
#[inline]
#[deprecated(
note = "Use `without_default_http_service_handler` instead",
since = "0.5.0"
)]
pub fn without_service_handler(self) -> Self {
self.without_default_http_service_handler()
}
#[cfg(feature = "http-client")]
#[inline]
pub fn with_http_timeout(mut self, timeout: Duration) -> Self {
self.http_timeout = Some(timeout);
self
}
#[cfg(feature = "http-client")]
#[inline]
pub fn with_http_redirection_limit(mut self, redirection_limit: usize) -> Self {
self.http_redirection_limit = redirection_limit;
self
}
#[inline]
pub fn with_custom_function(
mut self,
name: NamedNode,
evaluator: impl Fn(&[Term]) -> Option<Term> + Send + Sync + 'static,
) -> Self {
self.inner = self.inner.with_custom_function(name, evaluator);
self
}
#[inline]
pub fn with_custom_aggregate_function(
mut self,
name: NamedNode,
evaluator: impl Fn() -> Box<dyn AggregateFunctionAccumulator + Send + Sync>
+ Send
+ Sync
+ 'static,
) -> Self {
self.parser = self.parser.with_custom_aggregate_function(name.clone());
self.inner = self.inner.with_custom_aggregate_function(name, evaluator);
self
}
#[doc(hidden)]
#[inline]
pub fn without_optimizations(mut self) -> Self {
self.inner = self.inner.without_optimizations();
self
}
pub fn with_cancellation_token(mut self, cancellation_token: CancellationToken) -> Self {
self.inner = self.inner.with_cancellation_token(cancellation_token);
self
}
#[cfg_attr(not(feature = "http-client"), expect(unused_mut))]
fn into_evaluator(mut self) -> QueryEvaluator {
#[cfg(feature = "http-client")]
if self.with_http_default_service_handler {
self.inner = self
.inner
.with_default_service_handler(HttpServiceHandler::new(
self.http_timeout,
self.http_redirection_limit,
))
}
self.inner
}
pub fn parse_query(
mut self,
query: &(impl AsRef<str> + ?Sized),
) -> Result<PreparedSparqlQuery, SparqlSyntaxError> {
let query = take(&mut self.parser).parse_query(query.as_ref())?;
Ok(self.for_query(query))
}
#[expect(deprecated)]
pub fn for_query(self, query: impl Into<Query>) -> PreparedSparqlQuery {
let query = query.into();
PreparedSparqlQuery {
dataset: query.dataset,
query: query.inner,
evaluator: self.into_evaluator(),
substitutions: HashMap::new(),
}
}
pub fn parse_update(
mut self,
query: &(impl AsRef<str> + ?Sized),
) -> Result<PreparedSparqlUpdate, SparqlSyntaxError> {
let update = take(&mut self.parser).parse_update(query.as_ref())?;
Ok(self.for_update(update))
}
#[expect(deprecated)]
pub fn for_update(self, update: impl Into<Update>) -> PreparedSparqlUpdate {
#[cfg(feature = "http-client")]
let http_timeout = self.http_timeout;
#[cfg(feature = "http-client")]
let http_redirection_limit = self.http_redirection_limit;
PreparedSparqlUpdate::new(
self.into_evaluator(),
update.into(),
#[cfg(feature = "http-client")]
http_timeout,
#[cfg(feature = "http-client")]
http_redirection_limit,
)
}
}
impl Default for SparqlEvaluator {
fn default() -> Self {
Self {
#[cfg(feature = "http-client")]
http_timeout: None,
#[cfg(feature = "http-client")]
http_redirection_limit: 0,
#[cfg(feature = "http-client")]
with_http_default_service_handler: true,
parser: SparqlParser::new(),
inner: QueryEvaluator::new(),
}
}
}
#[derive(Clone)]
#[must_use]
pub struct PreparedSparqlQuery {
evaluator: QueryEvaluator,
query: spargebra::Query,
dataset: QueryDatasetSpecification,
substitutions: HashMap<Variable, Term>,
}
impl PreparedSparqlQuery {
#[inline]
pub fn substitute_variable(
mut self,
variable: impl Into<Variable>,
term: impl Into<Term>,
) -> Self {
self.substitutions.insert(variable.into(), term.into());
self
}
#[inline]
pub fn dataset(&self) -> &QueryDataset {
&self.dataset
}
#[inline]
pub fn dataset_mut(&mut self) -> &mut QueryDataset {
&mut self.dataset
}
pub fn on_store(self, store: &Store) -> BoundPreparedSparqlQuery<'static> {
let reader = store.storage().snapshot();
let queryable_dataset = DatasetView::new(reader);
self.on_queryable_dataset(queryable_dataset)
}
pub fn on_transaction<'b>(
self,
transaction: &'b Transaction<'_>,
) -> BoundPreparedSparqlQuery<'b> {
let reader = transaction.inner().reader();
let dataset = DatasetView::new(reader);
self.on_queryable_dataset(dataset)
}
pub fn on_queryable_dataset<'a, D: QueryableDataset<'a>>(
self,
queryable_dataset: D,
) -> BoundPreparedSparqlQuery<'a, D> {
BoundPreparedSparqlQuery {
evaluator: self.evaluator,
query: self.query,
queryable_dataset,
substitutions: self.substitutions,
dataset: self.dataset,
marker: PhantomData,
}
}
}
#[must_use]
pub struct BoundPreparedSparqlQuery<'a, D: QueryableDataset<'a> = DatasetView<'a>> {
evaluator: QueryEvaluator,
query: spargebra::Query,
queryable_dataset: D,
substitutions: HashMap<Variable, Term>,
dataset: QueryDatasetSpecification,
marker: PhantomData<&'a ()>,
}
impl<'a, D: QueryableDataset<'a>> BoundPreparedSparqlQuery<'a, D> {
#[inline]
pub fn substitute_variable(
mut self,
variable: impl Into<Variable>,
term: impl Into<Term>,
) -> Self {
self.substitutions.insert(variable.into(), term.into());
self
}
pub fn execute(self) -> Result<QueryResults<'a>, QueryEvaluationError> {
let mut prepared = self.evaluator.prepare(&self.query);
for (variable, term) in self.substitutions {
prepared = prepared.substitute_variable(variable, term);
}
*prepared.dataset_mut() = self.dataset;
prepared.execute(self.queryable_dataset)
}
pub fn compute_statistics(mut self) -> Self {
self.evaluator = self.evaluator.compute_statistics();
self
}
pub fn explain(
self,
) -> (
Result<QueryResults<'a>, QueryEvaluationError>,
QueryExplanation,
) {
let mut prepared = self.evaluator.prepare(&self.query);
for (variable, term) in self.substitutions {
prepared = prepared.substitute_variable(variable, term);
}
*prepared.dataset_mut() = self.dataset;
prepared.explain(self.queryable_dataset)
}
}