Makes SPARQL query execution static and simplify federation

pull/46/head
Tpt 4 years ago
parent 36bc870ca8
commit a3dce12d94
  1. 5
      js/src/store.rs
  2. 3
      lib/src/lib.rs
  3. 27
      lib/src/sparql/algebra.rs
  4. 832
      lib/src/sparql/eval.rs
  5. 2
      lib/src/sparql/json_results.rs
  6. 130
      lib/src/sparql/mod.rs
  7. 38
      lib/src/sparql/model.rs
  8. 58
      lib/src/sparql/parser.rs
  9. 79
      lib/src/sparql/plan.rs
  10. 142
      lib/src/sparql/plan_builder.rs
  11. 9
      lib/src/sparql/xml_results.rs
  12. 49
      lib/src/store/memory.rs
  13. 33
      lib/src/store/rocksdb.rs
  14. 31
      lib/src/store/sled.rs
  15. 262
      lib/tests/service_test_cases.rs
  16. 11
      python/src/memory_store.rs
  17. 7
      python/src/sled_store.rs
  18. 49
      python/src/store_utils.rs
  19. 16
      server/src/main.rs
  20. 43
      testsuite/src/sparql_evaluator.rs
  21. 16
      wikibase/src/main.rs

@ -99,11 +99,10 @@ impl JsMemoryStore {
} }
pub fn query(&self, query: &str) -> Result<JsValue, JsValue> { pub fn query(&self, query: &str) -> Result<JsValue, JsValue> {
let query = self let results = self
.store .store
.prepare_query(query, QueryOptions::default()) .query(query, QueryOptions::default())
.map_err(to_err)?; .map_err(to_err)?;
let results = query.exec().map_err(to_err)?;
let output = match results { let output = match results {
QueryResult::Solutions(solutions) => { QueryResult::Solutions(solutions) => {
let results = Array::new(); let results = Array::new();

@ -32,8 +32,7 @@
//! assert_eq!(vec![quad], results); //! assert_eq!(vec![quad], results);
//! //!
//! // SPARQL query //! // SPARQL query
//! let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; //! if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
//! if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? {
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); //! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! } //! }
//! # Result::Ok(()) //! # Result::Ok(())

@ -7,6 +7,7 @@ use rio_api::model as rio;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt; use std::fmt;
use std::ops::Add; use std::ops::Add;
use std::rc::Rc;
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum NamedNodeOrVariable { pub enum NamedNodeOrVariable {
@ -1305,25 +1306,25 @@ const EMPTY_DATASET: DatasetSpec = DatasetSpec {
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum QueryVariants { pub enum QueryVariants {
Select { Select {
dataset: DatasetSpec, dataset: Rc<DatasetSpec>,
algebra: GraphPattern, algebra: Rc<GraphPattern>,
base_iri: Option<Iri<String>>, base_iri: Option<Rc<Iri<String>>>,
}, },
Construct { Construct {
construct: Vec<TriplePattern>, construct: Rc<Vec<TriplePattern>>,
dataset: DatasetSpec, dataset: Rc<DatasetSpec>,
algebra: GraphPattern, algebra: Rc<GraphPattern>,
base_iri: Option<Iri<String>>, base_iri: Option<Rc<Iri<String>>>,
}, },
Describe { Describe {
dataset: DatasetSpec, dataset: Rc<DatasetSpec>,
algebra: GraphPattern, algebra: Rc<GraphPattern>,
base_iri: Option<Iri<String>>, base_iri: Option<Rc<Iri<String>>>,
}, },
Ask { Ask {
dataset: DatasetSpec, dataset: Rc<DatasetSpec>,
algebra: GraphPattern, algebra: Rc<GraphPattern>,
base_iri: Option<Iri<String>>, base_iri: Option<Rc<Iri<String>>>,
}, },
} }

File diff suppressed because it is too large Load Diff

@ -6,7 +6,7 @@ use crate::Error;
use crate::Result; use crate::Result;
use std::io::Write; use std::io::Write;
pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Result<W> { pub fn write_json_results<W: Write>(results: QueryResult, mut sink: W) -> Result<W> {
match results { match results {
QueryResult::Boolean(value) => { QueryResult::Boolean(value) => {
sink.write_all(b"{\"head\":{},\"boolean\":")?; sink.write_all(b"{\"head\":{},\"boolean\":")?;

@ -19,17 +19,17 @@ use crate::store::ReadableEncodedStore;
use crate::Error; use crate::Error;
use crate::Result; use crate::Result;
pub use crate::sparql::algebra::GraphPattern;
pub use crate::sparql::model::QuerySolution; pub use crate::sparql::model::QuerySolution;
pub use crate::sparql::model::QuerySolutionsIterator; pub use crate::sparql::model::QuerySolutionsIterator;
#[deprecated(note = "Please directly use QuerySolutionsIterator type instead")] #[deprecated(note = "Please directly use QuerySolutionsIterator type instead")]
pub type BindingsIterator<'a> = QuerySolutionsIterator<'a>; pub type BindingsIterator<'a> = QuerySolutionsIterator;
pub use crate::sparql::model::QueryResult; pub use crate::sparql::model::QueryResult;
pub use crate::sparql::model::QueryResultSyntax; pub use crate::sparql::model::QueryResultSyntax;
pub use crate::sparql::model::Variable; pub use crate::sparql::model::Variable;
pub use crate::sparql::parser::Query; pub use crate::sparql::parser::Query;
pub use crate::sparql::parser::SparqlParseError; pub use crate::sparql::parser::SparqlParseError;
use std::convert::TryInto; use std::convert::TryInto;
use std::rc::Rc;
/// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/)
#[deprecated( #[deprecated(
@ -38,44 +38,47 @@ use std::convert::TryInto;
pub trait PreparedQuery {} pub trait PreparedQuery {}
/// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/)
pub(crate) struct SimplePreparedQuery<S: ReadableEncodedStore>(SimplePreparedQueryAction<S>); pub(crate) struct SimplePreparedQuery<S: ReadableEncodedStore + 'static>(
SimplePreparedQueryAction<S>,
);
enum SimplePreparedQueryAction<S: ReadableEncodedStore> { #[derive(Clone)]
enum SimplePreparedQueryAction<S: ReadableEncodedStore + 'static> {
Select { Select {
plan: PlanNode, plan: Rc<PlanNode>,
variables: Vec<Variable>, variables: Rc<Vec<Variable>>,
evaluator: SimpleEvaluator<S>, evaluator: SimpleEvaluator<S>,
}, },
Ask { Ask {
plan: PlanNode, plan: Rc<PlanNode>,
evaluator: SimpleEvaluator<S>, evaluator: SimpleEvaluator<S>,
}, },
Construct { Construct {
plan: PlanNode, plan: Rc<PlanNode>,
construct: Vec<TripleTemplate>, construct: Rc<Vec<TripleTemplate>>,
evaluator: SimpleEvaluator<S>, evaluator: SimpleEvaluator<S>,
}, },
Describe { Describe {
plan: PlanNode, plan: Rc<PlanNode>,
evaluator: SimpleEvaluator<S>, evaluator: SimpleEvaluator<S>,
}, },
} }
impl<S: ReadableEncodedStore> SimplePreparedQuery<S> { impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
pub(crate) fn new( pub(crate) fn new(
store: S, store: S,
query: impl TryInto<Query, Error = impl Into<Error>>, query: impl TryInto<Query, Error = impl Into<Error>>,
options: QueryOptions, options: QueryOptions,
) -> Result<Self> { ) -> Result<Self> {
let dataset = DatasetView::new(store, options.default_graph_as_union); let dataset = Rc::new(DatasetView::new(store, options.default_graph_as_union));
Ok(Self(match query.try_into().map_err(|e| e.into())?.0 { Ok(Self(match query.try_into().map_err(|e| e.into())?.0 {
QueryVariants::Select { QueryVariants::Select {
algebra, base_iri, .. algebra, base_iri, ..
} => { } => {
let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?; let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Select { SimplePreparedQueryAction::Select {
plan, plan: Rc::new(plan),
variables, variables: Rc::new(variables),
evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler),
} }
} }
@ -84,7 +87,7 @@ impl<S: ReadableEncodedStore> SimplePreparedQuery<S> {
} => { } => {
let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Ask { SimplePreparedQueryAction::Ask {
plan, plan: Rc::new(plan),
evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler),
} }
} }
@ -96,12 +99,12 @@ impl<S: ReadableEncodedStore> SimplePreparedQuery<S> {
} => { } => {
let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?; let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Construct { SimplePreparedQueryAction::Construct {
plan, plan: Rc::new(plan),
construct: PlanBuilder::build_graph_template( construct: Rc::new(PlanBuilder::build_graph_template(
dataset.encoder(), dataset.encoder(),
&construct, &construct,
variables, variables,
)?, )?),
evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler),
} }
} }
@ -110,42 +113,27 @@ impl<S: ReadableEncodedStore> SimplePreparedQuery<S> {
} => { } => {
let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Describe { SimplePreparedQueryAction::Describe {
plan, plan: Rc::new(plan),
evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler),
} }
} }
})) }))
} }
/// Builds `SimplePreparedQuery` from an existing `GraphPattern`. This is used to support federated queries via `SERVICE` clauses
pub(crate) fn new_from_pattern(
store: S,
pattern: &GraphPattern,
options: QueryOptions,
) -> Result<Self> {
let dataset = DatasetView::new(store, options.default_graph_as_union);
let (plan, variables) = PlanBuilder::build(dataset.encoder(), pattern)?;
Ok(Self(SimplePreparedQueryAction::Select {
plan,
variables,
evaluator: SimpleEvaluator::new(dataset, None, options.service_handler),
}))
}
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult<'_>> { pub fn exec(&self) -> Result<QueryResult> {
match &self.0 { match &self.0 {
SimplePreparedQueryAction::Select { SimplePreparedQueryAction::Select {
plan, plan,
variables, variables,
evaluator, evaluator,
} => evaluator.evaluate_select_plan(plan, variables), } => evaluator.evaluate_select_plan(plan, variables.clone()),
SimplePreparedQueryAction::Ask { plan, evaluator } => evaluator.evaluate_ask_plan(plan), SimplePreparedQueryAction::Ask { plan, evaluator } => evaluator.evaluate_ask_plan(plan),
SimplePreparedQueryAction::Construct { SimplePreparedQueryAction::Construct {
plan, plan,
construct, construct,
evaluator, evaluator,
} => evaluator.evaluate_construct_plan(plan, construct), } => evaluator.evaluate_construct_plan(plan, construct.clone()),
SimplePreparedQueryAction::Describe { plan, evaluator } => { SimplePreparedQueryAction::Describe { plan, evaluator } => {
evaluator.evaluate_describe_plan(plan) evaluator.evaluate_describe_plan(plan)
} }
@ -156,50 +144,65 @@ impl<S: ReadableEncodedStore> SimplePreparedQuery<S> {
/// Handler for SPARQL SERVICEs. /// Handler for SPARQL SERVICEs.
/// ///
/// Might be used to implement [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/) /// Might be used to implement [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/)
///
/// ```
/// use oxigraph::model::*;
/// use oxigraph::{MemoryStore, Result};
/// use oxigraph::sparql::{QueryOptions, QueryResult, ServiceHandler, Query};
///
/// #[derive(Default)]
/// struct TestServiceHandler {
/// store: MemoryStore
/// }
///
/// impl ServiceHandler for TestServiceHandler {
/// fn handle(&self,service_name: NamedNode, query: Query) -> Result<QueryResult> {
/// if service_name == "http://example.com/service" {
/// self.store.query(query, QueryOptions::default())
/// } else {
/// panic!()
/// }
/// }
/// }
///
/// let store = MemoryStore::new();
/// let service = TestServiceHandler::default();
/// let ex = NamedNode::new("http://example.com")?;
/// service.store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None));
///
/// if let QueryResult::Solutions(mut solutions) = store.query(
/// "SELECT ?s WHERE { SERVICE <http://example.com/service> { ?s ?p ?o } }",
/// QueryOptions::default().with_service_handler(service)
/// )? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// }
/// # Result::Ok(())
/// ```
pub trait ServiceHandler { pub trait ServiceHandler {
/// Evaluates a `GraphPattern` against a given service identified by a `NamedNode`. /// Evaluates a `Query` against a given service identified by a `NamedNode`.
fn handle<'a>( fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResult>;
&'a self,
service_name: &NamedNode,
graph_pattern: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>>;
}
impl<F: for<'a> Fn(&NamedNode, &'a GraphPattern) -> Result<QuerySolutionsIterator<'a>>>
ServiceHandler for F
{
fn handle<'a>(
&'a self,
service_name: &NamedNode,
graph_pattern: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
self(service_name, graph_pattern)
}
} }
struct EmptyServiceHandler; struct EmptyServiceHandler;
impl ServiceHandler for EmptyServiceHandler { impl ServiceHandler for EmptyServiceHandler {
fn handle<'a>( fn handle(&self, _: NamedNode, _: Query) -> Result<QueryResult> {
&'a self,
_: &NamedNode,
_: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
Err(Error::msg("The SERVICE feature is not implemented")) Err(Error::msg("The SERVICE feature is not implemented"))
} }
} }
/// Options for SPARQL query evaluation /// Options for SPARQL query evaluation
#[derive(Clone)]
pub struct QueryOptions { pub struct QueryOptions {
pub(crate) default_graph_as_union: bool, pub(crate) default_graph_as_union: bool,
pub(crate) service_handler: Box<dyn ServiceHandler>, pub(crate) service_handler: Rc<dyn ServiceHandler>,
} }
impl Default for QueryOptions { impl Default for QueryOptions {
fn default() -> Self { fn default() -> Self {
Self { Self {
default_graph_as_union: false, default_graph_as_union: false,
service_handler: Box::new(EmptyServiceHandler), service_handler: Rc::new(EmptyServiceHandler),
} }
} }
} }
@ -211,8 +214,9 @@ impl QueryOptions {
self self
} }
/// Use a given `ServiceHandler` to execute SPARQL SERVICE calls
pub fn with_service_handler(mut self, service_handler: impl ServiceHandler + 'static) -> Self { pub fn with_service_handler(mut self, service_handler: impl ServiceHandler + 'static) -> Self {
self.service_handler = Box::new(service_handler); self.service_handler = Rc::new(service_handler);
self self
} }
} }

@ -12,17 +12,17 @@ use std::io::{BufRead, Write};
use std::rc::Rc; use std::rc::Rc;
/// Results of a [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// Results of a [SPARQL query](https://www.w3.org/TR/sparql11-query/)
pub enum QueryResult<'a> { pub enum QueryResult {
/// Results of a [SELECT](https://www.w3.org/TR/sparql11-query/#select) query /// Results of a [SELECT](https://www.w3.org/TR/sparql11-query/#select) query
Solutions(QuerySolutionsIterator<'a>), Solutions(QuerySolutionsIterator),
/// Result of a [ASK](https://www.w3.org/TR/sparql11-query/#ask) query /// Result of a [ASK](https://www.w3.org/TR/sparql11-query/#ask) query
Boolean(bool), Boolean(bool),
/// Results of a [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct) or [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe) query /// Results of a [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct) or [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe) query
Graph(Box<dyn Iterator<Item = Result<Triple>> + 'a>), Graph(Box<dyn Iterator<Item = Result<Triple>>>),
} }
impl<'a> QueryResult<'a> { impl QueryResult {
pub fn read(reader: impl BufRead + 'a, syntax: QueryResultSyntax) -> Result<Self> { pub fn read(reader: impl BufRead + 'static, syntax: QueryResultSyntax) -> Result<Self> {
match syntax { match syntax {
QueryResultSyntax::Xml => read_xml_results(reader), QueryResultSyntax::Xml => read_xml_results(reader),
QueryResultSyntax::Json => Err(Error::msg( QueryResultSyntax::Json => Err(Error::msg(
@ -127,28 +127,24 @@ impl FileSyntax for QueryResultSyntax {
/// use oxigraph::sparql::{QueryResult, QueryOptions}; /// use oxigraph::sparql::{QueryResult, QueryOptions};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// if let QueryResult::Solutions(solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// if let QueryResult::Solutions(solutions) = prepared_query.exec()? {
/// for solution in solutions { /// for solution in solutions {
/// println!("{:?}", solution?.get("s")); /// println!("{:?}", solution?.get("s"));
/// } /// }
/// } /// }
/// # Result::Ok(()) /// # Result::Ok(())
/// ``` /// ```
pub struct QuerySolutionsIterator<'a> { pub struct QuerySolutionsIterator {
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>>,
} }
impl<'a> QuerySolutionsIterator<'a> { impl QuerySolutionsIterator {
pub fn new( pub fn new(
variables: Vec<Variable>, variables: Rc<Vec<Variable>>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>>,
) -> Self { ) -> Self {
Self { Self { variables, iter }
variables: Rc::new(variables),
iter,
}
} }
/// The variables used in the solutions /// The variables used in the solutions
@ -158,8 +154,7 @@ impl<'a> QuerySolutionsIterator<'a> {
/// use oxigraph::sparql::{QueryResult, QueryOptions, Variable}; /// use oxigraph::sparql::{QueryResult, QueryOptions, Variable};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// let prepared_query = store.prepare_query("SELECT ?s ?o WHERE { ?s ?p ?o }", QueryOptions::default())?; /// if let QueryResult::Solutions(solutions) = store.query("SELECT ?s ?o WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// if let QueryResult::Solutions(solutions) = prepared_query.exec()? {
/// assert_eq!(solutions.variables(), &[Variable::new("s"), Variable::new("o")]); /// assert_eq!(solutions.variables(), &[Variable::new("s"), Variable::new("o")]);
/// } /// }
/// # Result::Ok(()) /// # Result::Ok(())
@ -169,21 +164,22 @@ impl<'a> QuerySolutionsIterator<'a> {
} }
#[deprecated(note = "Please directly use QuerySolutionsIterator as an iterator instead")] #[deprecated(note = "Please directly use QuerySolutionsIterator as an iterator instead")]
pub fn into_values_iter(self) -> Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a> { pub fn into_values_iter(self) -> Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>> {
self.iter self.iter
} }
#[deprecated(note = "Please directly use QuerySolutionsIterator as an iterator instead")]
pub fn destruct( pub fn destruct(
self, self,
) -> ( ) -> (
Vec<Variable>, Vec<Variable>,
Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>, Box<dyn Iterator<Item = Result<Vec<Option<Term>>>>>,
) { ) {
((*self.variables).clone(), self.iter) ((*self.variables).clone(), self.iter)
} }
} }
impl<'a> Iterator for QuerySolutionsIterator<'a> { impl Iterator for QuerySolutionsIterator {
type Item = Result<QuerySolution>; type Item = Result<QuerySolution>;
fn next(&mut self) -> Option<Result<QuerySolution>> { fn next(&mut self) -> Option<Result<QuerySolution>> {

@ -11,11 +11,23 @@ use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::error::Error; use std::error::Error;
use std::rc::Rc;
use std::str::Chars; use std::str::Chars;
use std::str::FromStr; use std::str::FromStr;
use std::{char, fmt}; use std::{char, fmt};
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/)
///
/// ```
/// # use oxigraph::Result;
/// use oxigraph::sparql::Query;
///
/// let query_str = "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }";
/// let query = Query::parse(query_str, None)?;
///
/// assert_eq!(query.to_string(), query_str);
/// # Result::Ok(())
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Query(pub(crate) QueryVariants); pub struct Query(pub(crate) QueryVariants);
@ -26,15 +38,15 @@ impl fmt::Display for Query {
} }
impl Query { impl Query {
/// Parses a SPARQL query /// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query
pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, SparqlParseError> { pub fn parse(query: &str, base_iri: Option<&str>) -> Result<Self, SparqlParseError> {
let mut state = ParserState { let mut state = ParserState {
base_iri: if let Some(base_iri) = base_iri { base_iri: if let Some(base_iri) = base_iri {
Some( Some(Rc::new(Iri::parse(base_iri.to_owned()).map_err(|e| {
Iri::parse(base_iri.to_owned()).map_err(|e| SparqlParseError { SparqlParseError {
inner: SparqlParseErrorKind::InvalidBaseIri(e), inner: SparqlParseErrorKind::InvalidBaseIri(e),
})?, }
) })?))
} else { } else {
None None
}, },
@ -379,7 +391,7 @@ enum Either<L, R> {
} }
pub struct ParserState { pub struct ParserState {
base_iri: Option<Iri<String>>, base_iri: Option<Rc<Iri<String>>>,
namespaces: HashMap<String, String>, namespaces: HashMap<String, String>,
used_bnodes: HashSet<BlankNode>, used_bnodes: HashSet<BlankNode>,
currently_used_bnodes: HashSet<BlankNode>, currently_used_bnodes: HashSet<BlankNode>,
@ -632,7 +644,7 @@ parser! {
//[5] //[5]
rule BaseDecl() = i("BASE") _ i:IRIREF() { rule BaseDecl() = i("BASE") _ i:IRIREF() {
state.base_iri = Some(i) state.base_iri = Some(Rc::new(i))
} }
//[6] //[6]
@ -643,8 +655,8 @@ parser! {
//[7] //[7]
rule SelectQuery() -> QueryVariants = s:SelectClause() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { //TODO: Modifier rule SelectQuery() -> QueryVariants = s:SelectClause() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { //TODO: Modifier
QueryVariants::Select { QueryVariants::Select {
dataset: d, dataset: Rc::new(d),
algebra: build_select(s, w, g, h, o, l, v, state), algebra: Rc::new(build_select(s, w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} }
@ -679,21 +691,21 @@ parser! {
rule ConstructQuery() -> QueryVariants = rule ConstructQuery() -> QueryVariants =
i("CONSTRUCT") _ c:ConstructTemplate() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { i("CONSTRUCT") _ c:ConstructTemplate() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Construct { QueryVariants::Construct {
construct: c, construct: Rc::new(c),
dataset: d, dataset: Rc::new(d),
algebra: build_select(Selection::default(), w, g, h, o, l, v, state), algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} / } /
i("CONSTRUCT") _ d:DatasetClauses() _ i("WHERE") _ "{" _ c:ConstructQuery_optional_triple_template() _ "}" _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { i("CONSTRUCT") _ d:DatasetClauses() _ i("WHERE") _ "{" _ c:ConstructQuery_optional_triple_template() _ "}" _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Construct { QueryVariants::Construct {
construct: c.clone(), construct: Rc::new(c.clone()),
dataset: d, dataset: Rc::new(d),
algebra: build_select( algebra: Rc::new(build_select(
Selection::default(), Selection::default(),
GraphPattern::BGP(c.into_iter().map(TripleOrPathPattern::from).collect()), GraphPattern::BGP(c.into_iter().map(TripleOrPathPattern::from).collect()),
g, h, o, l, v, state g, h, o, l, v, state
), )),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} }
@ -704,21 +716,21 @@ parser! {
rule DescribeQuery() -> QueryVariants = rule DescribeQuery() -> QueryVariants =
i("DESCRIBE") _ "*" _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { i("DESCRIBE") _ "*" _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Describe { QueryVariants::Describe {
dataset: d, dataset: Rc::new(d),
algebra: build_select(Selection::default(), w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state), algebra: Rc::new(build_select(Selection::default(), w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} / } /
i("DESCRIBE") _ p:DescribeQuery_item()+ _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { i("DESCRIBE") _ p:DescribeQuery_item()+ _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Describe { QueryVariants::Describe {
dataset: d, dataset: Rc::new(d),
algebra: build_select(Selection { algebra: Rc::new(build_select(Selection {
option: SelectionOption::Default, option: SelectionOption::Default,
variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri { variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri {
NamedNodeOrVariable::NamedNode(n) => SelectionMember::Expression(n.into(), Variable::new_random()), NamedNodeOrVariable::NamedNode(n) => SelectionMember::Expression(n.into(), Variable::new_random()),
NamedNodeOrVariable::Variable(v) => SelectionMember::Variable(v) NamedNodeOrVariable::Variable(v) => SelectionMember::Variable(v)
}).collect()) }).collect())
}, w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state), }, w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} }
@ -727,8 +739,8 @@ parser! {
//[12] //[12]
rule AskQuery() -> QueryVariants = i("ASK") _ d:DatasetClauses() w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { rule AskQuery() -> QueryVariants = i("ASK") _ d:DatasetClauses() w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Ask { QueryVariants::Ask {
dataset: d, dataset: Rc::new(d),
algebra: build_select(Selection::default(), w, g, h, o, l, v, state), algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone() base_iri: state.base_iri.clone()
} }
} }

@ -1,6 +1,6 @@
use crate::error::UnwrapInfallible; use crate::error::UnwrapInfallible;
use crate::sparql::algebra::GraphPattern;
use crate::sparql::model::Variable; use crate::sparql::model::Variable;
use crate::sparql::GraphPattern;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrHash, StrLookup, EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrHash, StrLookup,
ENCODED_DEFAULT_GRAPH, ENCODED_DEFAULT_GRAPH,
@ -9,6 +9,7 @@ use crate::store::ReadableEncodedStore;
use crate::Result; use crate::Result;
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::rc::Rc;
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum PlanNode { pub enum PlanNode {
@ -18,74 +19,74 @@ pub enum PlanNode {
}, },
Service { Service {
service_name: PatternValue, service_name: PatternValue,
variables: Vec<Variable>, variables: Rc<Vec<Variable>>,
child: Box<PlanNode>, child: Rc<PlanNode>,
graph_pattern: Box<GraphPattern>, graph_pattern: Rc<GraphPattern>,
silent: bool, silent: bool,
}, },
QuadPatternJoin { QuadPatternJoin {
child: Box<PlanNode>, child: Rc<PlanNode>,
subject: PatternValue, subject: PatternValue,
predicate: PatternValue, predicate: PatternValue,
object: PatternValue, object: PatternValue,
graph_name: PatternValue, graph_name: PatternValue,
}, },
PathPatternJoin { PathPatternJoin {
child: Box<PlanNode>, child: Rc<PlanNode>,
subject: PatternValue, subject: PatternValue,
path: PlanPropertyPath, path: Rc<PlanPropertyPath>,
object: PatternValue, object: PatternValue,
graph_name: PatternValue, graph_name: PatternValue,
}, },
Join { Join {
left: Box<PlanNode>, left: Rc<PlanNode>,
right: Box<PlanNode>, right: Rc<PlanNode>,
}, },
AntiJoin { AntiJoin {
left: Box<PlanNode>, left: Rc<PlanNode>,
right: Box<PlanNode>, right: Rc<PlanNode>,
}, },
Filter { Filter {
child: Box<PlanNode>, child: Rc<PlanNode>,
expression: PlanExpression, expression: Rc<PlanExpression>,
}, },
Union { Union {
children: Vec<PlanNode>, children: Vec<Rc<PlanNode>>,
}, },
LeftJoin { LeftJoin {
left: Box<PlanNode>, left: Rc<PlanNode>,
right: Box<PlanNode>, right: Rc<PlanNode>,
possible_problem_vars: Vec<usize>, //Variables that should not be part of the entry of the left join possible_problem_vars: Rc<Vec<usize>>, //Variables that should not be part of the entry of the left join
}, },
Extend { Extend {
child: Box<PlanNode>, child: Rc<PlanNode>,
position: usize, position: usize,
expression: PlanExpression, expression: Rc<PlanExpression>,
}, },
Sort { Sort {
child: Box<PlanNode>, child: Rc<PlanNode>,
by: Vec<Comparator>, by: Vec<Comparator>,
}, },
HashDeduplicate { HashDeduplicate {
child: Box<PlanNode>, child: Rc<PlanNode>,
}, },
Skip { Skip {
child: Box<PlanNode>, child: Rc<PlanNode>,
count: usize, count: usize,
}, },
Limit { Limit {
child: Box<PlanNode>, child: Rc<PlanNode>,
count: usize, count: usize,
}, },
Project { Project {
child: Box<PlanNode>, child: Rc<PlanNode>,
mapping: Vec<(usize, usize)>, // pairs of (variable key in child, variable key in output) mapping: Rc<Vec<(usize, usize)>>, // pairs of (variable key in child, variable key in output)
}, },
Aggregate { Aggregate {
// By definition the group by key are the range 0..key_mapping.len() // By definition the group by key are the range 0..key_mapping.len()
child: Box<PlanNode>, child: Rc<PlanNode>,
key_mapping: Vec<(usize, usize)>, // aggregate key pairs of (variable key in child, variable key in output) key_mapping: Rc<Vec<(usize, usize)>>, // aggregate key pairs of (variable key in child, variable key in output)
aggregates: Vec<(PlanAggregation, usize)>, aggregates: Rc<Vec<(PlanAggregation, usize)>>,
}, },
} }
@ -153,7 +154,7 @@ impl PlanNode {
child.add_maybe_bound_variables(set); child.add_maybe_bound_variables(set);
} }
PlanNode::Union { children } => { PlanNode::Union { children } => {
for child in children { for child in children.iter() {
child.add_maybe_bound_variables(set); child.add_maybe_bound_variables(set);
} }
} }
@ -191,7 +192,7 @@ impl PlanNode {
.. ..
} => { } => {
set.extend(key_mapping.iter().map(|(_, o)| o)); set.extend(key_mapping.iter().map(|(_, o)| o));
for (_, var) in aggregates { for (_, var) in aggregates.iter() {
set.insert(*var); set.insert(*var);
} }
} }
@ -218,7 +219,7 @@ impl PatternValue {
pub enum PlanExpression { pub enum PlanExpression {
Constant(EncodedTerm), Constant(EncodedTerm),
Variable(usize), Variable(usize),
Exists(Box<PlanNode>), Exists(Rc<PlanNode>),
Or(Box<PlanExpression>, Box<PlanExpression>), Or(Box<PlanExpression>, Box<PlanExpression>),
And(Box<PlanExpression>, Box<PlanExpression>), And(Box<PlanExpression>, Box<PlanExpression>),
Equal(Box<PlanExpression>, Box<PlanExpression>), Equal(Box<PlanExpression>, Box<PlanExpression>),
@ -447,19 +448,19 @@ pub enum PlanAggregationFunction {
Max, Max,
Avg, Avg,
Sample, Sample,
GroupConcat { separator: String }, GroupConcat { separator: Rc<String> },
} }
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum PlanPropertyPath { pub enum PlanPropertyPath {
PredicatePath(EncodedTerm), PredicatePath(EncodedTerm),
InversePath(Box<PlanPropertyPath>), InversePath(Rc<PlanPropertyPath>),
SequencePath(Box<PlanPropertyPath>, Box<PlanPropertyPath>), SequencePath(Rc<PlanPropertyPath>, Rc<PlanPropertyPath>),
AlternativePath(Box<PlanPropertyPath>, Box<PlanPropertyPath>), AlternativePath(Rc<PlanPropertyPath>, Rc<PlanPropertyPath>),
ZeroOrMorePath(Box<PlanPropertyPath>), ZeroOrMorePath(Rc<PlanPropertyPath>),
OneOrMorePath(Box<PlanPropertyPath>), OneOrMorePath(Rc<PlanPropertyPath>),
ZeroOrOnePath(Box<PlanPropertyPath>), ZeroOrOnePath(Rc<PlanPropertyPath>),
NegatedPropertySet(Vec<EncodedTerm>), NegatedPropertySet(Rc<Vec<EncodedTerm>>),
} }
#[derive(Eq, PartialEq, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]

@ -6,6 +6,7 @@ use crate::store::numeric_encoder::{Encoder, ENCODED_DEFAULT_GRAPH};
use crate::Error; use crate::Error;
use crate::Result; use crate::Result;
use std::collections::{BTreeSet, HashSet}; use std::collections::{BTreeSet, HashSet};
use std::rc::Rc;
pub struct PlanBuilder<E: Encoder> { pub struct PlanBuilder<E: Encoder> {
encoder: E, encoder: E,
@ -39,8 +40,8 @@ impl<E: Encoder> PlanBuilder<E> {
Ok(match pattern { Ok(match pattern {
GraphPattern::BGP(p) => self.build_for_bgp(p, variables, graph_name)?, GraphPattern::BGP(p) => self.build_for_bgp(p, variables, graph_name)?,
GraphPattern::Join(a, b) => PlanNode::Join { GraphPattern::Join(a, b) => PlanNode::Join {
left: Box::new(self.build_for_graph_pattern(a, variables, graph_name)?), left: Rc::new(self.build_for_graph_pattern(a, variables, graph_name)?),
right: Box::new(self.build_for_graph_pattern(b, variables, graph_name)?), right: Rc::new(self.build_for_graph_pattern(b, variables, graph_name)?),
}, },
GraphPattern::LeftJoin(a, b, e) => { GraphPattern::LeftJoin(a, b, e) => {
let left = self.build_for_graph_pattern(a, variables, graph_name)?; let left = self.build_for_graph_pattern(a, variables, graph_name)?;
@ -52,22 +53,22 @@ impl<E: Encoder> PlanBuilder<E> {
//We add the extra filter if needed //We add the extra filter if needed
let right = if let Some(e) = e { let right = if let Some(e) = e {
PlanNode::Filter { PlanNode::Filter {
child: Box::new(right), child: Rc::new(right),
expression: self.build_for_expression(e, variables, graph_name)?, expression: Rc::new(self.build_for_expression(e, variables, graph_name)?),
} }
} else { } else {
right right
}; };
PlanNode::LeftJoin { PlanNode::LeftJoin {
left: Box::new(left), left: Rc::new(left),
right: Box::new(right), right: Rc::new(right),
possible_problem_vars: possible_problem_vars.into_iter().collect(), possible_problem_vars: Rc::new(possible_problem_vars.into_iter().collect()),
} }
} }
GraphPattern::Filter(e, p) => PlanNode::Filter { GraphPattern::Filter(e, p) => PlanNode::Filter {
child: Box::new(self.build_for_graph_pattern(p, variables, graph_name)?), child: Rc::new(self.build_for_graph_pattern(p, variables, graph_name)?),
expression: self.build_for_expression(e, variables, graph_name)?, expression: Rc::new(self.build_for_expression(e, variables, graph_name)?),
}, },
GraphPattern::Union(a, b) => { GraphPattern::Union(a, b) => {
//We flatten the UNIONs //We flatten the UNIONs
@ -80,9 +81,9 @@ impl<E: Encoder> PlanBuilder<E> {
stack.push(a); stack.push(a);
stack.push(b); stack.push(b);
} }
Some(p) => { Some(p) => children.push(Rc::new(
children.push(self.build_for_graph_pattern(p, variables, graph_name)?) self.build_for_graph_pattern(p, variables, graph_name)?,
} )),
} }
} }
PlanNode::Union { children } PlanNode::Union { children }
@ -92,13 +93,13 @@ impl<E: Encoder> PlanBuilder<E> {
self.build_for_graph_pattern(p, variables, graph_name)? self.build_for_graph_pattern(p, variables, graph_name)?
} }
GraphPattern::Extend(p, v, e) => PlanNode::Extend { GraphPattern::Extend(p, v, e) => PlanNode::Extend {
child: Box::new(self.build_for_graph_pattern(p, variables, graph_name)?), child: Rc::new(self.build_for_graph_pattern(p, variables, graph_name)?),
position: variable_key(variables, v), position: variable_key(variables, v),
expression: self.build_for_expression(e, variables, graph_name)?, expression: Rc::new(self.build_for_expression(e, variables, graph_name)?),
}, },
GraphPattern::Minus(a, b) => PlanNode::AntiJoin { GraphPattern::Minus(a, b) => PlanNode::AntiJoin {
left: Box::new(self.build_for_graph_pattern(a, variables, graph_name)?), left: Rc::new(self.build_for_graph_pattern(a, variables, graph_name)?),
right: Box::new(self.build_for_graph_pattern(b, variables, graph_name)?), right: Rc::new(self.build_for_graph_pattern(b, variables, graph_name)?),
}, },
GraphPattern::Service(n, p, s) => { GraphPattern::Service(n, p, s) => {
// Child building should be at the begging in order for `variables` to be filled // Child building should be at the begging in order for `variables` to be filled
@ -106,9 +107,9 @@ impl<E: Encoder> PlanBuilder<E> {
let service_name = self.pattern_value_from_named_node_or_variable(n, variables)?; let service_name = self.pattern_value_from_named_node_or_variable(n, variables)?;
PlanNode::Service { PlanNode::Service {
service_name, service_name,
variables: variables.clone(), variables: Rc::new(variables.clone()),
child: Box::new(child), child: Rc::new(child),
graph_pattern: Box::new(*p.clone()), graph_pattern: Rc::new(*p.clone()),
silent: *s, silent: *s,
} }
} }
@ -118,29 +119,32 @@ impl<E: Encoder> PlanBuilder<E> {
self.convert_pattern_value_id(graph_name, variables, &mut inner_variables); self.convert_pattern_value_id(graph_name, variables, &mut inner_variables);
PlanNode::Aggregate { PlanNode::Aggregate {
child: Box::new(self.build_for_graph_pattern( child: Rc::new(self.build_for_graph_pattern(
p, p,
&mut inner_variables, &mut inner_variables,
inner_graph_name, inner_graph_name,
)?), )?),
key_mapping: key key_mapping: Rc::new(
.iter() key.iter()
.map(|k| { .map(|k| {
( (
variable_key(&mut inner_variables, k), variable_key(&mut inner_variables, k),
variable_key(variables, k), variable_key(variables, k),
) )
}) })
.collect(), .collect(),
aggregates: aggregates ),
.iter() aggregates: Rc::new(
.map(|(a, v)| { aggregates
Ok(( .iter()
self.build_for_aggregate(a, &mut inner_variables, graph_name)?, .map(|(a, v)| {
variable_key(variables, v), Ok((
)) self.build_for_aggregate(a, &mut inner_variables, graph_name)?,
}) variable_key(variables, v),
.collect::<Result<Vec<_>>>()?, ))
})
.collect::<Result<Vec<_>>>()?,
),
} }
} }
GraphPattern::Data(bs) => PlanNode::StaticBindings { GraphPattern::Data(bs) => PlanNode::StaticBindings {
@ -159,7 +163,7 @@ impl<E: Encoder> PlanBuilder<E> {
}) })
.collect(); .collect();
PlanNode::Sort { PlanNode::Sort {
child: Box::new(self.build_for_graph_pattern(l, variables, graph_name)?), child: Rc::new(self.build_for_graph_pattern(l, variables, graph_name)?),
by: by?, by: by?,
} }
} }
@ -168,35 +172,37 @@ impl<E: Encoder> PlanBuilder<E> {
let inner_graph_name = let inner_graph_name =
self.convert_pattern_value_id(graph_name, variables, &mut inner_variables); self.convert_pattern_value_id(graph_name, variables, &mut inner_variables);
PlanNode::Project { PlanNode::Project {
child: Box::new(self.build_for_graph_pattern( child: Rc::new(self.build_for_graph_pattern(
l, l,
&mut inner_variables, &mut inner_variables,
inner_graph_name, inner_graph_name,
)?), )?),
mapping: new_variables mapping: Rc::new(
.iter() new_variables
.enumerate() .iter()
.map(|(new_variable, variable)| { .enumerate()
(new_variable, variable_key(variables, variable)) .map(|(new_variable, variable)| {
}) (new_variable, variable_key(variables, variable))
.collect(), })
.collect(),
),
} }
} }
GraphPattern::Distinct(l) => PlanNode::HashDeduplicate { GraphPattern::Distinct(l) => PlanNode::HashDeduplicate {
child: Box::new(self.build_for_graph_pattern(l, variables, graph_name)?), child: Rc::new(self.build_for_graph_pattern(l, variables, graph_name)?),
}, },
GraphPattern::Reduced(l) => self.build_for_graph_pattern(l, variables, graph_name)?, GraphPattern::Reduced(l) => self.build_for_graph_pattern(l, variables, graph_name)?,
GraphPattern::Slice(l, start, length) => { GraphPattern::Slice(l, start, length) => {
let mut plan = self.build_for_graph_pattern(l, variables, graph_name)?; let mut plan = self.build_for_graph_pattern(l, variables, graph_name)?;
if *start > 0 { if *start > 0 {
plan = PlanNode::Skip { plan = PlanNode::Skip {
child: Box::new(plan), child: Rc::new(plan),
count: *start, count: *start,
}; };
} }
if let Some(length) = length { if let Some(length) = length {
plan = PlanNode::Limit { plan = PlanNode::Limit {
child: Box::new(plan), child: Rc::new(plan),
count: *length, count: *length,
}; };
} }
@ -215,7 +221,7 @@ impl<E: Encoder> PlanBuilder<E> {
for pattern in sort_bgp(p) { for pattern in sort_bgp(p) {
plan = match pattern { plan = match pattern {
TripleOrPathPattern::Triple(pattern) => PlanNode::QuadPatternJoin { TripleOrPathPattern::Triple(pattern) => PlanNode::QuadPatternJoin {
child: Box::new(plan), child: Rc::new(plan),
subject: self subject: self
.pattern_value_from_term_or_variable(&pattern.subject, variables)?, .pattern_value_from_term_or_variable(&pattern.subject, variables)?,
predicate: self predicate: self
@ -224,10 +230,10 @@ impl<E: Encoder> PlanBuilder<E> {
graph_name, graph_name,
}, },
TripleOrPathPattern::Path(pattern) => PlanNode::PathPatternJoin { TripleOrPathPattern::Path(pattern) => PlanNode::PathPatternJoin {
child: Box::new(plan), child: Rc::new(plan),
subject: self subject: self
.pattern_value_from_term_or_variable(&pattern.subject, variables)?, .pattern_value_from_term_or_variable(&pattern.subject, variables)?,
path: self.build_for_path(&pattern.path)?, path: Rc::new(self.build_for_path(&pattern.path)?),
object: self.pattern_value_from_term_or_variable(&pattern.object, variables)?, object: self.pattern_value_from_term_or_variable(&pattern.object, variables)?,
graph_name, graph_name,
}, },
@ -242,30 +248,30 @@ impl<E: Encoder> PlanBuilder<E> {
self.encoder.encode_named_node(p).map_err(|e| e.into())?, self.encoder.encode_named_node(p).map_err(|e| e.into())?,
), ),
PropertyPath::InversePath(p) => { PropertyPath::InversePath(p) => {
PlanPropertyPath::InversePath(Box::new(self.build_for_path(p)?)) PlanPropertyPath::InversePath(Rc::new(self.build_for_path(p)?))
} }
PropertyPath::AlternativePath(a, b) => PlanPropertyPath::AlternativePath( PropertyPath::AlternativePath(a, b) => PlanPropertyPath::AlternativePath(
Box::new(self.build_for_path(a)?), Rc::new(self.build_for_path(a)?),
Box::new(self.build_for_path(b)?), Rc::new(self.build_for_path(b)?),
), ),
PropertyPath::SequencePath(a, b) => PlanPropertyPath::SequencePath( PropertyPath::SequencePath(a, b) => PlanPropertyPath::SequencePath(
Box::new(self.build_for_path(a)?), Rc::new(self.build_for_path(a)?),
Box::new(self.build_for_path(b)?), Rc::new(self.build_for_path(b)?),
), ),
PropertyPath::ZeroOrMorePath(p) => { PropertyPath::ZeroOrMorePath(p) => {
PlanPropertyPath::ZeroOrMorePath(Box::new(self.build_for_path(p)?)) PlanPropertyPath::ZeroOrMorePath(Rc::new(self.build_for_path(p)?))
} }
PropertyPath::OneOrMorePath(p) => { PropertyPath::OneOrMorePath(p) => {
PlanPropertyPath::OneOrMorePath(Box::new(self.build_for_path(p)?)) PlanPropertyPath::OneOrMorePath(Rc::new(self.build_for_path(p)?))
} }
PropertyPath::ZeroOrOnePath(p) => { PropertyPath::ZeroOrOnePath(p) => {
PlanPropertyPath::ZeroOrOnePath(Box::new(self.build_for_path(p)?)) PlanPropertyPath::ZeroOrOnePath(Rc::new(self.build_for_path(p)?))
} }
PropertyPath::NegatedPropertySet(p) => PlanPropertyPath::NegatedPropertySet( PropertyPath::NegatedPropertySet(p) => PlanPropertyPath::NegatedPropertySet(Rc::new(
p.iter() p.iter()
.map(|p| self.encoder.encode_named_node(p).map_err(|e| e.into())) .map(|p| self.encoder.encode_named_node(p).map_err(|e| e.into()))
.collect::<Result<Vec<_>>>()?, .collect::<Result<Vec<_>>>()?,
), )),
}) })
} }
@ -678,7 +684,7 @@ impl<E: Encoder> PlanBuilder<E> {
} }
}, },
Expression::Bound(v) => PlanExpression::Bound(variable_key(variables, v)), Expression::Bound(v) => PlanExpression::Bound(variable_key(variables, v)),
Expression::Exists(n) => PlanExpression::Exists(Box::new( Expression::Exists(n) => PlanExpression::Exists(Rc::new(
self.build_for_graph_pattern(n, variables, graph_name)?, self.build_for_graph_pattern(n, variables, graph_name)?,
)), )),
}) })
@ -822,7 +828,7 @@ impl<E: Encoder> PlanBuilder<E> {
}, },
Aggregation::GroupConcat(e, distinct, separator) => PlanAggregation { Aggregation::GroupConcat(e, distinct, separator) => PlanAggregation {
function: PlanAggregationFunction::GroupConcat { function: PlanAggregationFunction::GroupConcat {
separator: separator.clone().unwrap_or_else(|| " ".to_string()), separator: Rc::new(separator.clone().unwrap_or_else(|| " ".to_string())),
}, },
parameter: Some(self.build_for_expression(e, variables, graph_name)?), parameter: Some(self.build_for_expression(e, variables, graph_name)?),
distinct: *distinct, distinct: *distinct,
@ -936,8 +942,8 @@ impl<E: Encoder> PlanBuilder<E> {
self.add_left_join_problematic_variables(&*child, set); self.add_left_join_problematic_variables(&*child, set);
} }
PlanNode::Union { children } => { PlanNode::Union { children } => {
for child in children { for child in children.iter() {
self.add_left_join_problematic_variables(&*child, set); self.add_left_join_problematic_variables(child, set);
} }
} }
PlanNode::Join { left, right, .. } => { PlanNode::Join { left, right, .. } => {
@ -981,7 +987,7 @@ impl<E: Encoder> PlanBuilder<E> {
} => { } => {
set.extend(key_mapping.iter().map(|(_, o)| o)); set.extend(key_mapping.iter().map(|(_, o)| o));
//TODO: This is too harsh //TODO: This is too harsh
for (_, var) in aggregates { for (_, var) in aggregates.iter() {
set.insert(*var); set.insert(*var);
} }
} }

@ -15,8 +15,9 @@ use std::collections::BTreeMap;
use std::io::BufRead; use std::io::BufRead;
use std::io::Write; use std::io::Write;
use std::iter::empty; use std::iter::empty;
use std::rc::Rc;
pub fn write_xml_results<W: Write>(results: QueryResult<'_>, sink: W) -> Result<W> { pub fn write_xml_results<W: Write>(results: QueryResult, sink: W) -> Result<W> {
let mut writer = Writer::new(sink); let mut writer = Writer::new(sink);
match results { match results {
QueryResult::Boolean(value) => { QueryResult::Boolean(value) => {
@ -102,7 +103,7 @@ pub fn write_xml_results<W: Write>(results: QueryResult<'_>, sink: W) -> Result<
Ok(writer.into_inner()) Ok(writer.into_inner())
} }
pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>> { pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult> {
enum State { enum State {
Start, Start,
Sparql, Sparql,
@ -171,7 +172,7 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
mapping.insert(var.as_bytes().to_vec(), i); mapping.insert(var.as_bytes().to_vec(), i);
} }
return Ok(QueryResult::Solutions(QuerySolutionsIterator::new( return Ok(QueryResult::Solutions(QuerySolutionsIterator::new(
variables.into_iter().map(Variable::new).collect(), Rc::new(variables.into_iter().map(Variable::new).collect()),
Box::new(ResultsIterator { Box::new(ResultsIterator {
reader, reader,
buffer: Vec::default(), buffer: Vec::default(),
@ -209,7 +210,7 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
State::AfterHead => { State::AfterHead => {
if event.name() == b"results" { if event.name() == b"results" {
return Ok(QueryResult::Solutions(QuerySolutionsIterator::new( return Ok(QueryResult::Solutions(QuerySolutionsIterator::new(
variables.into_iter().map(Variable::new).collect(), Rc::new(variables.into_iter().map(Variable::new).collect()),
Box::new(empty()), Box::new(empty()),
))) )))
} else { } else {

@ -2,7 +2,7 @@
use crate::error::UnwrapInfallible; use crate::error::UnwrapInfallible;
use crate::model::*; use crate::model::*;
use crate::sparql::{GraphPattern, Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{Query, QueryOptions, QueryResult, SimplePreparedQuery};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::store::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore}; use crate::store::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore};
use crate::{DatasetSyntax, Error, GraphSyntax}; use crate::{DatasetSyntax, Error, GraphSyntax};
@ -39,8 +39,7 @@ use std::vec::IntoIter;
/// assert_eq!(vec![quad], results); /// assert_eq!(vec![quad], results);
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # Result::Ok(()) /// # Result::Ok(())
@ -82,7 +81,36 @@ impl MemoryStore {
new new
} }
/// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
///
/// Usage example:
/// ```
/// use oxigraph::model::*;
/// use oxigraph::{MemoryStore, Result};
/// use oxigraph::sparql::{QueryOptions, QueryResult};
///
/// let store = MemoryStore::new();
///
/// // insertions
/// let ex = NamedNode::new("http://example.com")?;
/// store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None));
///
/// // SPARQL query
/// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// }
/// # Result::Ok(())
/// ```
pub fn query(
&self,
query: impl TryInto<Query, Error = impl Into<Error>>,
options: QueryOptions,
) -> crate::Result<QueryResult> {
self.prepare_query(query, options)?.exec()
}
/// Prepares a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) and returns an object that could be used to execute it. /// Prepares a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) and returns an object that could be used to execute it.
/// It is useful if you want to execute multiple times the same SPARQL query.
/// ///
/// Usage example: /// Usage example:
/// ``` /// ```
@ -115,19 +143,6 @@ impl MemoryStore {
)?)) )?))
} }
/// This is similar to `prepare_query`, but useful if a SPARQL query has already been parsed, which is the case when building `ServiceHandler`s for federated queries with `SERVICE` clauses. For examples, look in the tests.
pub fn prepare_query_from_pattern(
&self,
graph_pattern: &GraphPattern,
options: QueryOptions,
) -> crate::Result<MemoryPreparedQuery> {
Ok(MemoryPreparedQuery(SimplePreparedQuery::new_from_pattern(
self.clone(),
graph_pattern,
options,
)?))
}
/// Retrieves quads with a filter on each quad component /// Retrieves quads with a filter on each quad component
/// ///
/// Usage example: /// Usage example:
@ -863,7 +878,7 @@ pub struct MemoryPreparedQuery(SimplePreparedQuery<MemoryStore>);
impl MemoryPreparedQuery { impl MemoryPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> crate::Result<QueryResult<'_>> { pub fn exec(&self) -> crate::Result<QueryResult> {
self.0.exec() self.0.exec()
} }
} }

@ -2,7 +2,7 @@
use crate::error::UnwrapInfallible; use crate::error::UnwrapInfallible;
use crate::model::*; use crate::model::*;
use crate::sparql::{GraphPattern, Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{Query, QueryOptions, QueryResult, SimplePreparedQuery};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::store::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore}; use crate::store::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore};
use crate::{DatasetSyntax, GraphSyntax, Result}; use crate::{DatasetSyntax, GraphSyntax, Result};
@ -39,9 +39,7 @@ use std::{fmt, str};
/// assert_eq!(vec![quad], results?); /// assert_eq!(vec![quad], results?);
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// let results = prepared_query.exec()?;
/// if let QueryResult::Solutions(mut solutions) = results {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # /// #
@ -89,30 +87,29 @@ impl RocksDbStore {
Ok(new) Ok(new)
} }
/// Prepares a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) and returns an object that could be used to execute it. /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
/// ///
/// See `MemoryStore` for a usage example. /// See `MemoryStore` for a usage example.
pub fn prepare_query( pub fn query(
&self, &self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>, query: impl TryInto<Query, Error = impl Into<crate::Error>>,
options: QueryOptions, options: QueryOptions,
) -> Result<RocksDbPreparedQuery> { ) -> Result<QueryResult> {
Ok(RocksDbPreparedQuery(SimplePreparedQuery::new( self.prepare_query(query, options)?.exec()
(*self).clone(),
query,
options,
)?))
} }
/// This is similar to `prepare_query`, but useful if a SPARQL query has already been parsed, which is the case when building `ServiceHandler`s for federated queries with `SERVICE` clauses. For examples, look in the tests. /// Prepares a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) and returns an object that could be used to execute it.
pub fn prepare_query_from_pattern( /// It is useful if you want to execute multiple times the same SPARQL query.
///
/// See `MemoryStore` for a usage example.
pub fn prepare_query(
&self, &self,
graph_pattern: &GraphPattern, query: impl TryInto<Query, Error = impl Into<crate::Error>>,
options: QueryOptions, options: QueryOptions,
) -> Result<RocksDbPreparedQuery> { ) -> Result<RocksDbPreparedQuery> {
Ok(RocksDbPreparedQuery(SimplePreparedQuery::new_from_pattern( Ok(RocksDbPreparedQuery(SimplePreparedQuery::new(
(*self).clone(), (*self).clone(),
graph_pattern, query,
options, options,
)?)) )?))
} }
@ -511,7 +508,7 @@ pub struct RocksDbPreparedQuery(SimplePreparedQuery<RocksDbStore>);
impl RocksDbPreparedQuery { impl RocksDbPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult<'_>> { pub fn exec(&self) -> Result<QueryResult> {
self.0.exec() self.0.exec()
} }
} }

@ -2,7 +2,7 @@
use crate::error::UnwrapInfallible; use crate::error::UnwrapInfallible;
use crate::model::*; use crate::model::*;
use crate::sparql::{GraphPattern, Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{Query, QueryOptions, QueryResult, SimplePreparedQuery};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::store::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore}; use crate::store::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore};
use crate::{DatasetSyntax, Error, GraphSyntax, Result}; use crate::{DatasetSyntax, Error, GraphSyntax, Result};
@ -39,8 +39,7 @@ use std::{fmt, str};
/// assert_eq!(vec![quad], results?); /// assert_eq!(vec![quad], results?);
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// }; /// };
/// # /// #
@ -84,30 +83,28 @@ impl SledStore {
Ok(new) Ok(new)
} }
/// Prepares a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) and returns an object that could be used to execute it. /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
/// ///
/// See `MemoryStore` for a usage example. /// See `MemoryStore` for a usage example.
pub fn prepare_query( pub fn query(
&self, &self,
query: impl TryInto<Query, Error = impl Into<Error>>, query: impl TryInto<Query, Error = impl Into<Error>>,
options: QueryOptions, options: QueryOptions,
) -> Result<SledPreparedQuery> { ) -> Result<QueryResult> {
Ok(SledPreparedQuery(SimplePreparedQuery::new( self.prepare_query(query, options)?.exec()
(*self).clone(),
query,
options,
)?))
} }
/// This is similar to `prepare_query`, but useful if a SPARQL query has already been parsed, which is the case when building `ServiceHandler`s for federated queries with `SERVICE` clauses. For examples, look in the tests. /// Prepares a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) and returns an object that could be used to execute it.
pub fn prepare_query_from_pattern( ///
/// See `MemoryStore` for a usage example.
pub fn prepare_query(
&self, &self,
graph_pattern: &GraphPattern, query: impl TryInto<Query, Error = impl Into<Error>>,
options: QueryOptions, options: QueryOptions,
) -> Result<SledPreparedQuery> { ) -> Result<SledPreparedQuery> {
Ok(SledPreparedQuery(SimplePreparedQuery::new_from_pattern( Ok(SledPreparedQuery(SimplePreparedQuery::new(
(*self).clone(), (*self).clone(),
graph_pattern, query,
options, options,
)?)) )?))
} }
@ -670,7 +667,7 @@ pub struct SledPreparedQuery(SimplePreparedQuery<SledStore>);
impl SledPreparedQuery { impl SledPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult<'_>> { pub fn exec(&self) -> Result<QueryResult> {
self.0.exec() self.0.exec()
} }
} }

@ -1,262 +0,0 @@
use oxigraph::model::*;
use oxigraph::sparql::*;
use oxigraph::*;
use std::io::BufRead;
#[test]
fn simple_service_test() {
struct TestServiceHandler;
impl ServiceHandler for TestServiceHandler {
fn handle<'a>(
&'a self,
_: &NamedNode,
graph_pattern: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
let triples =
b"<http://example.com/s> <http://example.com/p> <http://example.com/o> .".as_ref();
do_pattern(triples, graph_pattern, QueryOptions::default())
}
}
let query = r#"
SELECT ?s ?p ?o
WHERE
{
SERVICE <http://service1.org>
{ ?s ?p ?o
}
}
"#
.to_string();
let options = QueryOptions::default().with_service_handler(TestServiceHandler);
let collected = do_query(b"".as_ref(), &query, options)
.unwrap()
.map(|b| {
b.unwrap()
.iter()
.map(|(_, v)| v.clone())
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let solution = vec![vec![ex("s"), ex("p"), ex("o")]];
assert_eq!(collected, solution);
}
#[test]
fn two_service_test() {
#[derive(Clone, Copy)]
struct TwoServiceTest;
impl ServiceHandler for TwoServiceTest {
fn handle<'a>(
&'a self,
named_node: &NamedNode,
graph_pattern: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
let service1 = NamedNode::new("http://service1.org").unwrap();
let service2 = NamedNode::new("http://service2.org").unwrap();
if named_node == &service1 {
let triples = br#"
<http://example.com/bob> <http://xmlns.com/foaf/0.1/name> "Bob" .
<http://example.com/alice> <http://xmlns.com/foaf/0.1/name> "Alice" .
"#
.as_ref();
do_pattern(triples, graph_pattern, QueryOptions::default())
} else if named_node == &service2 {
let triples = br#"
<http://example.com/bob> <http://xmlns.com/foaf/0.1/mbox> <mailto:bob@example.com> .
<http://example.com/alice> <http://xmlns.com/foaf/0.1/mbox> <mailto:alice@example.com> .
"#
.as_ref();
do_pattern(triples, graph_pattern, QueryOptions::default())
} else {
Err(Error::msg("not found"))
}
}
}
let query = r#"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name ?mbox
WHERE
{
SERVICE <http://service1.org>
{ ?s foaf:name ?name
}
SERVICE <http://service2.org>
{ ?s foaf:mbox ?mbox
}
}
ORDER BY ?name
"#
.to_string();
let options = QueryOptions::default().with_service_handler(TwoServiceTest);
let collected = do_query(b"".as_ref(), &query, options)
.unwrap()
.map(|b| {
b.unwrap()
.iter()
.map(|(_, v)| v.clone())
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let solution = vec![
vec![literal("Alice"), mailto("alice@example.com")],
vec![literal("Bob"), mailto("bob@example.com")],
];
assert_eq!(collected, solution);
}
#[test]
fn silent_service_empty_set_test() {
#[derive(Clone, Copy)]
struct ServiceTest;
impl ServiceHandler for ServiceTest {
fn handle<'a>(
&'a self,
_: &NamedNode,
_: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
Err(Error::msg("This is supposed to fail"))
}
}
let query = r#"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name ?mbox
WHERE
{
SERVICE SILENT <http://service1.org>
{ ?s foaf:name ?name
}
}
ORDER BY ?name
"#
.to_string();
let triples = b"".as_ref();
let options = QueryOptions::default().with_service_handler(ServiceTest);
assert_eq!(do_query(triples, &query, options).unwrap().count(), 1);
}
#[test]
fn non_silent_service_test() {
#[derive(Clone, Copy)]
struct ServiceTest;
impl ServiceHandler for ServiceTest {
fn handle<'a>(
&'a self,
_: &NamedNode,
_: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
Err(Error::msg("This is supposed to fail"))
}
}
let query = r#"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name
WHERE
{
SERVICE <http://service1.org>
{ ?s foaf:name ?name
}
}
ORDER BY ?name
"#
.to_string();
let triples = b"".as_ref();
let options = QueryOptions::default().with_service_handler(ServiceTest);
let mut solutions = do_query(triples, &query, options).unwrap();
if let Some(Err(_)) = solutions.next() {
} else {
panic!("This should have been an error since the service fails")
}
}
fn ex(id: &str) -> Term {
Term::NamedNode(NamedNode::new(format!("http://example.com/{}", id)).unwrap())
}
fn mailto(id: &str) -> Term {
Term::NamedNode(NamedNode::new(format!("mailto:{}", id)).unwrap())
}
fn literal(str: &str) -> Term {
Term::Literal(Literal::new_simple_literal(str))
}
fn make_store(reader: impl BufRead) -> Result<MemoryStore> {
let store = MemoryStore::new();
store
.load_graph(
reader,
GraphSyntax::NTriples,
&GraphName::DefaultGraph,
None,
)
.unwrap();
Ok(store)
}
fn query_store(
store: MemoryStore,
query: &str,
options: QueryOptions,
) -> Result<QuerySolutionsIterator<'_>> {
match store.prepare_query(query, options)?.exec()? {
QueryResult::Solutions(iterator) => {
let (variables, iter) = iterator.destruct();
let collected = iter.collect::<Vec<_>>();
Ok(QuerySolutionsIterator::new(
variables,
Box::new(collected.into_iter()),
))
}
_ => Err(Error::msg("Excpected bindings but got another QueryResult")),
}
}
fn pattern_store<'a>(
store: MemoryStore,
pattern: &'a GraphPattern,
options: QueryOptions,
) -> Result<QuerySolutionsIterator<'a>> {
match store
.prepare_query_from_pattern(&pattern, options)?
.exec()?
{
QueryResult::Solutions(iterator) => {
let (varaibles, iter) = iterator.destruct();
let collected = iter.collect::<Vec<_>>();
Ok(QuerySolutionsIterator::new(
varaibles,
Box::new(collected.into_iter()),
))
}
_ => Err(Error::msg("Expected bindings but got another QueryResult")),
}
}
fn do_query(
reader: impl BufRead,
query: &str,
options: QueryOptions,
) -> Result<QuerySolutionsIterator<'_>> {
let store = make_store(reader)?;
query_store(store, query, options)
}
fn do_pattern<'a>(
reader: impl BufRead,
pattern: &'a GraphPattern,
options: QueryOptions,
) -> Result<QuerySolutionsIterator<'a>> {
let store = make_store(reader)?;
pattern_store(store, pattern, options)
}

@ -4,7 +4,7 @@ use oxigraph::model::*;
use oxigraph::sparql::QueryOptions; use oxigraph::sparql::QueryOptions;
use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, MemoryStore}; use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, MemoryStore};
use pyo3::basic::CompareOp; use pyo3::basic::CompareOp;
use pyo3::exceptions::{NotImplementedError, RuntimeError, ValueError}; use pyo3::exceptions::{NotImplementedError, ValueError};
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::PyTuple; use pyo3::types::PyTuple;
use pyo3::{PyIterProtocol, PyObjectProtocol, PySequenceProtocol}; use pyo3::{PyIterProtocol, PyObjectProtocol, PySequenceProtocol};
@ -55,14 +55,11 @@ impl PyMemoryStore {
} }
fn query(&self, query: &str, py: Python<'_>) -> PyResult<PyObject> { fn query(&self, query: &str, py: Python<'_>) -> PyResult<PyObject> {
let query = self let results = self
.inner .inner
.prepare_query(query, QueryOptions::default()) .query(query, QueryOptions::default())
.map_err(|e| ParseError::py_err(e.to_string()))?; .map_err(|e| ParseError::py_err(e.to_string()))?;
let results = query query_results_to_python(py, results)
.exec()
.map_err(|e| RuntimeError::py_err(e.to_string()))?;
query_results_to_python(py, results, RuntimeError::py_err)
} }
#[args(data, mime_type, "*", base_iri = "\"\"", to_graph = "None")] #[args(data, mime_type, "*", base_iri = "\"\"", to_graph = "None")]

@ -63,12 +63,11 @@ impl PySledStore {
} }
fn query(&self, query: &str, py: Python<'_>) -> PyResult<PyObject> { fn query(&self, query: &str, py: Python<'_>) -> PyResult<PyObject> {
let query = self let results = self
.inner .inner
.prepare_query(query, QueryOptions::default()) .query(query, QueryOptions::default())
.map_err(|e| ParseError::py_err(e.to_string()))?; .map_err(|e| ParseError::py_err(e.to_string()))?;
let results = query.exec().map_err(|e| SledError::py_err(e.to_string()))?; query_results_to_python(py, results)
query_results_to_python(py, results, SledError::py_err)
} }
#[args(data, mime_type, "*", base_iri = "\"\"", to_graph = "None")] #[args(data, mime_type, "*", base_iri = "\"\"", to_graph = "None")]

@ -1,12 +1,11 @@
use crate::model::*; use crate::model::*;
use oxigraph::model::*; use oxigraph::model::*;
use oxigraph::sparql::{QueryResult, QuerySolution}; use oxigraph::sparql::{QueryResult, QuerySolution, QuerySolutionsIterator};
use oxigraph::Result; use oxigraph::Result;
use pyo3::exceptions::TypeError; use pyo3::exceptions::{IOError, TypeError};
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::{create_exception, PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol}; use pyo3::{create_exception, PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol};
use std::fmt::Write; use std::fmt::Write;
use std::vec::IntoIter;
create_exception!(oxigraph, ParseError, pyo3::exceptions::Exception); create_exception!(oxigraph, ParseError, pyo3::exceptions::Exception);
@ -49,26 +48,10 @@ pub fn extract_quads_pattern(
)) ))
} }
pub fn query_results_to_python( pub fn query_results_to_python(py: Python<'_>, results: QueryResult) -> PyResult<PyObject> {
py: Python<'_>,
results: QueryResult<'_>,
error: impl Fn(String) -> PyErr,
) -> PyResult<PyObject> {
Ok(match results { Ok(match results {
QueryResult::Solutions(solutions) => QuerySolutionIter { QueryResult::Solutions(inner) => QuerySolutionIter { inner }.into_py(py),
inner: solutions QueryResult::Graph(inner) => TripleResultIter { inner }.into_py(py),
.collect::<Result<Vec<_>>>()
.map_err(|e| error(e.to_string()))?
.into_iter(),
}
.into_py(py),
QueryResult::Graph(triples) => TripleResultIter {
inner: triples
.collect::<Result<Vec<_>>>()
.map_err(|e| error(e.to_string()))?
.into_iter(),
}
.into_py(py),
QueryResult::Boolean(b) => b.into_py(py), QueryResult::Boolean(b) => b.into_py(py),
}) })
} }
@ -118,7 +101,7 @@ impl PyMappingProtocol for PyQuerySolution {
#[pyclass(unsendable)] #[pyclass(unsendable)]
pub struct QuerySolutionIter { pub struct QuerySolutionIter {
inner: IntoIter<QuerySolution>, inner: QuerySolutionsIterator,
} }
#[pyproto] #[pyproto]
@ -127,14 +110,19 @@ impl PyIterProtocol for QuerySolutionIter {
slf.into() slf.into()
} }
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyQuerySolution> { fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<PyQuerySolution>> {
slf.inner.next().map(move |inner| PyQuerySolution { inner }) Ok(slf
.inner
.next()
.transpose()
.map_err(|e| IOError::py_err(e.to_string()))? //TODO: improve
.map(move |inner| PyQuerySolution { inner }))
} }
} }
#[pyclass(unsendable)] #[pyclass(unsendable)]
pub struct TripleResultIter { pub struct TripleResultIter {
inner: IntoIter<Triple>, inner: Box<dyn Iterator<Item = Result<Triple>>>,
} }
#[pyproto] #[pyproto]
@ -143,7 +131,12 @@ impl PyIterProtocol for TripleResultIter {
slf.into() slf.into()
} }
fn __next__(mut slf: PyRefMut<Self>) -> Option<(PyObject, PyObject, PyObject)> { fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<(PyObject, PyObject, PyObject)>> {
slf.inner.next().map(move |t| triple_to_python(slf.py(), t)) Ok(slf
.inner
.next()
.transpose()
.map_err(|e| IOError::py_err(e.to_string()))? //TODO: improve
.map(move |t| triple_to_python(slf.py(), t)))
} }
} }

@ -17,7 +17,7 @@ use async_std::prelude::*;
use async_std::task::{block_on, spawn, spawn_blocking}; use async_std::task::{block_on, spawn, spawn_blocking};
use http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode}; use http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode};
use oxigraph::model::GraphName; use oxigraph::model::GraphName;
use oxigraph::sparql::{QueryOptions, QueryResult, QueryResultSyntax}; use oxigraph::sparql::{Query, QueryOptions, QueryResult, QueryResultSyntax};
use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, RocksDbStore}; use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, RocksDbStore};
use std::str::FromStr; use std::str::FromStr;
use url::form_urlencoded; use url::form_urlencoded;
@ -163,15 +163,13 @@ async fn evaluate_sparql_query(
request: Request, request: Request,
) -> Result<Response> { ) -> Result<Response> {
spawn_blocking(move || { spawn_blocking(move || {
let query = Query::parse(&query, None).map_err(|e| {
let mut e = Error::from(e);
e.set_status(StatusCode::BadRequest);
e
})?;
let results = store.query(query, QueryOptions::default())?;
//TODO: stream //TODO: stream
let query = store
.prepare_query(&query, QueryOptions::default())
.map_err(|e| {
let mut e = Error::from(e);
e.set_status(StatusCode::BadRequest);
e
})?;
let results = query.exec()?;
if let QueryResult::Graph(_) = results { if let QueryResult::Graph(_) = results {
let format = content_negotiation( let format = content_negotiation(
request, request,

@ -84,15 +84,12 @@ fn evaluate_sparql_test(test: &Test) -> Result<()> {
.ok_or_else(|| Error::msg(format!("No action found for test {}", test)))?; .ok_or_else(|| Error::msg(format!("No action found for test {}", test)))?;
let options = QueryOptions::default() let options = QueryOptions::default()
.with_service_handler(StaticServiceHandler::new(&test.service_data)?); .with_service_handler(StaticServiceHandler::new(&test.service_data)?);
match store.prepare_query( match Query::parse(&read_file_to_string(query_file)?, Some(query_file)) {
Query::parse(&read_file_to_string(query_file)?, Some(query_file))?,
options,
) {
Err(error) => Err(Error::msg(format!( Err(error) => Err(Error::msg(format!(
"Failure to parse query of {} with error: {}", "Failure to parse query of {} with error: {}",
test, error test, error
))), ))),
Ok(query) => match query.exec() { Ok(query) => match store.query(query, options) {
Err(error) => Err(Error::msg(format!( Err(error) => Err(Error::msg(format!(
"Failure to execute query of {} with error: {}", "Failure to execute query of {} with error: {}",
test, error test, error
@ -173,35 +170,18 @@ impl StaticServiceHandler {
} }
impl ServiceHandler for StaticServiceHandler { impl ServiceHandler for StaticServiceHandler {
fn handle<'a>( fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResult> {
&'a self, self.services
service_name: &NamedNode, .get(&service_name)
graph_pattern: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
if let QueryResult::Solutions(iterator) = self
.services
.get(service_name)
.ok_or_else(|| Error::msg(format!("Service {} not found", service_name)))? .ok_or_else(|| Error::msg(format!("Service {} not found", service_name)))?
.prepare_query_from_pattern( .query(
&graph_pattern, query,
QueryOptions::default().with_service_handler(self.clone()), QueryOptions::default().with_service_handler(self.clone()),
)? )
.exec()?
{
//TODO: very ugly
let (variables, iter) = iterator.destruct();
let collected = iter.collect::<Vec<_>>();
Ok(QuerySolutionsIterator::new(
variables,
Box::new(collected.into_iter()),
))
} else {
Err(Error::msg("Expected solutions but got another QueryResult"))
}
} }
} }
fn to_dataset(result: QueryResult<'_>, with_order: bool) -> Result<MemoryStore> { fn to_dataset(result: QueryResult, with_order: bool) -> Result<MemoryStore> {
match result { match result {
QueryResult::Graph(graph) => graph.map(|t| t.map(|t| t.in_graph(None))).collect(), QueryResult::Graph(graph) => graph.map(|t| t.map(|t| t.in_graph(None))).collect(),
QueryResult::Boolean(value) => { QueryResult::Boolean(value) => {
@ -377,10 +357,7 @@ impl fmt::Display for StaticQueryResults {
} }
impl StaticQueryResults { impl StaticQueryResults {
fn from_query_results( fn from_query_results(results: QueryResult, with_order: bool) -> Result<StaticQueryResults> {
results: QueryResult<'_>,
with_order: bool,
) -> Result<StaticQueryResults> {
Ok(Self::from_dataset(to_dataset(results, with_order)?)) Ok(Self::from_dataset(to_dataset(results, with_order)?))
} }

@ -16,7 +16,7 @@ use async_std::net::{TcpListener, TcpStream};
use async_std::prelude::*; use async_std::prelude::*;
use async_std::task::{spawn, spawn_blocking}; use async_std::task::{spawn, spawn_blocking};
use http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode}; use http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode};
use oxigraph::sparql::{QueryOptions, QueryResult, QueryResultSyntax}; use oxigraph::sparql::{Query, QueryOptions, QueryResult, QueryResultSyntax};
use oxigraph::{FileSyntax, GraphSyntax, RocksDbStore}; use oxigraph::{FileSyntax, GraphSyntax, RocksDbStore};
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
@ -174,14 +174,12 @@ async fn evaluate_sparql_query(
) -> Result<Response> { ) -> Result<Response> {
spawn_blocking(move || { spawn_blocking(move || {
//TODO: stream //TODO: stream
let query = store let query = Query::parse(&query, None).map_err(|e| {
.prepare_query(&query, QueryOptions::default()) let mut e = Error::from(e);
.map_err(|e| { e.set_status(StatusCode::BadRequest);
let mut e = Error::from(e); e
e.set_status(StatusCode::BadRequest); })?;
e let results = store.query(query, QueryOptions::default())?;
})?;
let results = query.exec()?;
if let QueryResult::Graph(_) = results { if let QueryResult::Graph(_) = results {
let format = content_negotiation( let format = content_negotiation(
request, request,

Loading…
Cancel
Save