Fork of https://github.com/oxigraph/oxigraph.git for the purpose of NextGraph project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
oxigraph/lib/src/sparql/mod.rs

208 lines
6.7 KiB

//! [SPARQL](https://www.w3.org/TR/sparql11-overview/) implementation.
mod algebra;
mod eval;
mod json_results;
mod model;
mod parser;
mod plan;
mod plan_builder;
mod xml_results;
use crate::model::NamedNode;
use crate::sparql::algebra::QueryVariants;
use crate::sparql::eval::SimpleEvaluator;
use crate::sparql::parser::read_sparql_query;
use crate::sparql::plan::TripleTemplate;
use crate::sparql::plan::{DatasetView, PlanNode};
use crate::sparql::plan_builder::PlanBuilder;
use crate::store::StoreConnection;
use crate::Result;
use std::fmt;
use rio_api::iri::Iri;
pub use crate::sparql::algebra::GraphPattern;
pub use crate::sparql::model::BindingsIterator;
pub use crate::sparql::model::QueryResult;
pub use crate::sparql::model::QueryResultSyntax;
pub use crate::sparql::model::Variable;
/// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/)
pub trait PreparedQuery {
/// Evaluates the query and returns its results
fn exec<'a>(&'a self, options: &'a QueryOptions<'a>) -> Result<QueryResult<'a>>;
}
/// An implementation of `PreparedQuery` for internal use
pub struct SimplePreparedQuery<S: StoreConnection>(SimplePreparedQueryAction<S>);
enum SimplePreparedQueryAction<S: StoreConnection> {
Select {
plan: PlanNode,
variables: Vec<Variable>,
evaluator: SimpleEvaluator<S>,
},
Ask {
plan: PlanNode,
evaluator: SimpleEvaluator<S>,
},
Construct {
plan: PlanNode,
construct: Vec<TripleTemplate>,
evaluator: SimpleEvaluator<S>,
},
Describe {
plan: PlanNode,
evaluator: SimpleEvaluator<S>,
},
}
impl<'a, S: StoreConnection + 'a> SimplePreparedQuery<S> {
pub(crate) fn new(connection: S, query: &str, options: &'a QueryOptions<'a>) -> Result<Self> {
let dataset = DatasetView::new(connection, options.default_graph_as_union);
//TODO avoid inserting terms in the Repository StringStore
Ok(Self(match read_sparql_query(query, options.base_iri)? {
QueryVariants::Select {
algebra,
dataset: _,
base_iri,
} => {
let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Select {
plan,
variables,
evaluator: SimpleEvaluator::new(dataset, base_iri),
}
}
QueryVariants::Ask {
algebra,
dataset: _,
base_iri,
} => {
let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Ask {
plan,
evaluator: SimpleEvaluator::new(dataset, base_iri),
}
}
QueryVariants::Construct {
construct,
algebra,
dataset: _,
base_iri,
} => {
let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Construct {
plan,
construct: PlanBuilder::build_graph_template(
dataset.encoder(),
&construct,
variables,
)?,
evaluator: SimpleEvaluator::new(dataset, base_iri),
}
}
QueryVariants::Describe {
algebra,
dataset: _,
base_iri,
} => {
let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Describe {
plan,
evaluator: SimpleEvaluator::new(dataset, base_iri),
}
}
}))
}
pub(crate) fn new_from_pattern(
connection: S,
pattern: &GraphPattern,
options: &'a QueryOptions<'a>
) -> Result<Self> {
let dataset = DatasetView::new(connection, options.default_graph_as_union);
let iri = options.base_iri.map(|i| Iri::parse(i.to_string()).unwrap());
let (plan, variables) = PlanBuilder::build(dataset.encoder(), pattern)?;
Ok(Self(SimplePreparedQueryAction::Select {
plan,
variables,
evaluator: SimpleEvaluator::new(dataset, iri),
}))
}
}
impl<S: StoreConnection> PreparedQuery for SimplePreparedQuery<S> {
fn exec<'a>(&'a self, options: &'a QueryOptions<'a>) -> Result<QueryResult<'a>> {
match &self.0 {
SimplePreparedQueryAction::Select {
plan,
variables,
evaluator,
} => evaluator.evaluate_select_plan(&plan, &variables, options),
SimplePreparedQueryAction::Ask { plan, evaluator } => {
evaluator.evaluate_ask_plan(&plan, options)
}
SimplePreparedQueryAction::Construct {
plan,
construct,
evaluator,
} => evaluator.evaluate_construct_plan(&plan, &construct, &options),
SimplePreparedQueryAction::Describe { plan, evaluator } => {
evaluator.evaluate_describe_plan(&plan, &options)
}
}
}
}
pub trait ServiceHandler {
fn handle<'a>(&'a self, node: NamedNode) -> Option<(fn(GraphPattern) -> Result<BindingsIterator<'a>>)>;
}
/// Options for SPARQL query parsing and evaluation like the query base IRI
pub struct QueryOptions<'a> {
pub(crate) base_iri: Option<&'a str>,
pub(crate) default_graph_as_union: bool,
pub(crate) service_handler: Option<Box<dyn ServiceHandler>>,
}
impl<'a> Default for QueryOptions<'a> {
fn default() -> Self {
Self {
base_iri: None,
default_graph_as_union: false,
service_handler: None as Option<Box<dyn ServiceHandler>>,
}
}
}
impl<'a> QueryOptions<'a> {
/// Allows to set the base IRI of the query
pub fn with_base_iri(mut self, base_iri: &'a str) -> Self {
self.base_iri = Some(base_iri);
self
}
/// Consider the union of all graphs in the repository as the default graph
pub fn with_default_graph_as_union(mut self) -> Self {
self.default_graph_as_union = true;
self
}
}
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/)
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Query(QueryVariants);
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl Query {
/// Parses a SPARQL query
pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self> {
Ok(Query(read_sparql_query(query, base_iri)?))
}
}