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> {
let query = self
let results = self
.store
.prepare_query(query, QueryOptions::default())
.query(query, QueryOptions::default())
.map_err(to_err)?;
let results = query.exec().map_err(to_err)?;
let output = match results {
QueryResult::Solutions(solutions) => {
let results = Array::new();

@ -32,8 +32,7 @@
//! assert_eq!(vec![quad], results);
//!
//! // SPARQL query
//! let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
//! if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? {
//! 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(())

@ -7,6 +7,7 @@ use rio_api::model as rio;
use std::collections::BTreeSet;
use std::fmt;
use std::ops::Add;
use std::rc::Rc;
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum NamedNodeOrVariable {
@ -1305,25 +1306,25 @@ const EMPTY_DATASET: DatasetSpec = DatasetSpec {
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum QueryVariants {
Select {
dataset: DatasetSpec,
algebra: GraphPattern,
base_iri: Option<Iri<String>>,
dataset: Rc<DatasetSpec>,
algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>,
},
Construct {
construct: Vec<TriplePattern>,
dataset: DatasetSpec,
algebra: GraphPattern,
base_iri: Option<Iri<String>>,
construct: Rc<Vec<TriplePattern>>,
dataset: Rc<DatasetSpec>,
algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>,
},
Describe {
dataset: DatasetSpec,
algebra: GraphPattern,
base_iri: Option<Iri<String>>,
dataset: Rc<DatasetSpec>,
algebra: Rc<GraphPattern>,
base_iri: Option<Rc<Iri<String>>>,
},
Ask {
dataset: DatasetSpec,
algebra: GraphPattern,
base_iri: Option<Iri<String>>,
dataset: Rc<DatasetSpec>,
algebra: Rc<GraphPattern>,
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 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 {
QueryResult::Boolean(value) => {
sink.write_all(b"{\"head\":{},\"boolean\":")?;

@ -19,17 +19,17 @@ use crate::store::ReadableEncodedStore;
use crate::Error;
use crate::Result;
pub use crate::sparql::algebra::GraphPattern;
pub use crate::sparql::model::QuerySolution;
pub use crate::sparql::model::QuerySolutionsIterator;
#[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::QueryResultSyntax;
pub use crate::sparql::model::Variable;
pub use crate::sparql::parser::Query;
pub use crate::sparql::parser::SparqlParseError;
use std::convert::TryInto;
use std::rc::Rc;
/// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/)
#[deprecated(
@ -38,44 +38,47 @@ use std::convert::TryInto;
pub trait PreparedQuery {}
/// 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 {
plan: PlanNode,
variables: Vec<Variable>,
plan: Rc<PlanNode>,
variables: Rc<Vec<Variable>>,
evaluator: SimpleEvaluator<S>,
},
Ask {
plan: PlanNode,
plan: Rc<PlanNode>,
evaluator: SimpleEvaluator<S>,
},
Construct {
plan: PlanNode,
construct: Vec<TripleTemplate>,
plan: Rc<PlanNode>,
construct: Rc<Vec<TripleTemplate>>,
evaluator: SimpleEvaluator<S>,
},
Describe {
plan: PlanNode,
plan: Rc<PlanNode>,
evaluator: SimpleEvaluator<S>,
},
}
impl<S: ReadableEncodedStore> SimplePreparedQuery<S> {
impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
pub(crate) fn new(
store: S,
query: impl TryInto<Query, Error = impl Into<Error>>,
options: QueryOptions,
) -> 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 {
QueryVariants::Select {
algebra, base_iri, ..
} => {
let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryAction::Select {
plan,
variables,
plan: Rc::new(plan),
variables: Rc::new(variables),
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)?;
SimplePreparedQueryAction::Ask {
plan,
plan: Rc::new(plan),
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)?;
SimplePreparedQueryAction::Construct {
plan,
construct: PlanBuilder::build_graph_template(
plan: Rc::new(plan),
construct: Rc::new(PlanBuilder::build_graph_template(
dataset.encoder(),
&construct,
variables,
)?,
)?),
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)?;
SimplePreparedQueryAction::Describe {
plan,
plan: Rc::new(plan),
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
pub fn exec(&self) -> Result<QueryResult<'_>> {
pub fn exec(&self) -> Result<QueryResult> {
match &self.0 {
SimplePreparedQueryAction::Select {
plan,
variables,
evaluator,
} => evaluator.evaluate_select_plan(plan, variables),
} => evaluator.evaluate_select_plan(plan, variables.clone()),
SimplePreparedQueryAction::Ask { plan, evaluator } => evaluator.evaluate_ask_plan(plan),
SimplePreparedQueryAction::Construct {
plan,
construct,
evaluator,
} => evaluator.evaluate_construct_plan(plan, construct),
} => evaluator.evaluate_construct_plan(plan, construct.clone()),
SimplePreparedQueryAction::Describe { plan, evaluator } => {
evaluator.evaluate_describe_plan(plan)
}
@ -156,50 +144,65 @@ impl<S: ReadableEncodedStore> SimplePreparedQuery<S> {
/// Handler for SPARQL SERVICEs.
///
/// 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 {
/// Evaluates a `GraphPattern` against a given service identified by a `NamedNode`.
fn handle<'a>(
&'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)
}
/// Evaluates a `Query` against a given service identified by a `NamedNode`.
fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResult>;
}
struct EmptyServiceHandler;
impl ServiceHandler for EmptyServiceHandler {
fn handle<'a>(
&'a self,
_: &NamedNode,
_: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
fn handle(&self, _: NamedNode, _: Query) -> Result<QueryResult> {
Err(Error::msg("The SERVICE feature is not implemented"))
}
}
/// Options for SPARQL query evaluation
#[derive(Clone)]
pub struct QueryOptions {
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 {
fn default() -> Self {
Self {
default_graph_as_union: false,
service_handler: Box::new(EmptyServiceHandler),
service_handler: Rc::new(EmptyServiceHandler),
}
}
}
@ -211,8 +214,9 @@ impl QueryOptions {
self
}
/// Use a given `ServiceHandler` to execute SPARQL SERVICE calls
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
}
}

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

@ -11,11 +11,23 @@ use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::convert::TryFrom;
use std::error::Error;
use std::rc::Rc;
use std::str::Chars;
use std::str::FromStr;
use std::{char, fmt};
/// 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)]
pub struct Query(pub(crate) QueryVariants);
@ -26,15 +38,15 @@ impl fmt::Display for 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> {
let mut state = ParserState {
base_iri: if let Some(base_iri) = base_iri {
Some(
Iri::parse(base_iri.to_owned()).map_err(|e| SparqlParseError {
Some(Rc::new(Iri::parse(base_iri.to_owned()).map_err(|e| {
SparqlParseError {
inner: SparqlParseErrorKind::InvalidBaseIri(e),
})?,
)
}
})?))
} else {
None
},
@ -379,7 +391,7 @@ enum Either<L, R> {
}
pub struct ParserState {
base_iri: Option<Iri<String>>,
base_iri: Option<Rc<Iri<String>>>,
namespaces: HashMap<String, String>,
used_bnodes: HashSet<BlankNode>,
currently_used_bnodes: HashSet<BlankNode>,
@ -632,7 +644,7 @@ parser! {
//[5]
rule BaseDecl() = i("BASE") _ i:IRIREF() {
state.base_iri = Some(i)
state.base_iri = Some(Rc::new(i))
}
//[6]
@ -643,8 +655,8 @@ parser! {
//[7]
rule SelectQuery() -> QueryVariants = s:SelectClause() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() { //TODO: Modifier
QueryVariants::Select {
dataset: d,
algebra: build_select(s, w, g, h, o, l, v, state),
dataset: Rc::new(d),
algebra: Rc::new(build_select(s, w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone()
}
}
@ -679,21 +691,21 @@ parser! {
rule ConstructQuery() -> QueryVariants =
i("CONSTRUCT") _ c:ConstructTemplate() _ d:DatasetClauses() _ w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Construct {
construct: c,
dataset: d,
algebra: build_select(Selection::default(), w, g, h, o, l, v, state),
construct: Rc::new(c),
dataset: Rc::new(d),
algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)),
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() {
QueryVariants::Construct {
construct: c.clone(),
dataset: d,
algebra: build_select(
construct: Rc::new(c.clone()),
dataset: Rc::new(d),
algebra: Rc::new(build_select(
Selection::default(),
GraphPattern::BGP(c.into_iter().map(TripleOrPathPattern::from).collect()),
g, h, o, l, v, state
),
)),
base_iri: state.base_iri.clone()
}
}
@ -704,21 +716,21 @@ parser! {
rule DescribeQuery() -> QueryVariants =
i("DESCRIBE") _ "*" _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Describe {
dataset: d,
algebra: build_select(Selection::default(), w.unwrap_or_else(GraphPattern::default), g, h, o, l, v, state),
dataset: Rc::new(d),
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()
}
} /
i("DESCRIBE") _ p:DescribeQuery_item()+ _ d:DatasetClauses() w:WhereClause()? _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Describe {
dataset: d,
algebra: build_select(Selection {
dataset: Rc::new(d),
algebra: Rc::new(build_select(Selection {
option: SelectionOption::Default,
variables: Some(p.into_iter().map(|var_or_iri| match var_or_iri {
NamedNodeOrVariable::NamedNode(n) => SelectionMember::Expression(n.into(), Variable::new_random()),
NamedNodeOrVariable::Variable(v) => SelectionMember::Variable(v)
}).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()
}
}
@ -727,8 +739,8 @@ parser! {
//[12]
rule AskQuery() -> QueryVariants = i("ASK") _ d:DatasetClauses() w:WhereClause() _ g:GroupClause()? _ h:HavingClause()? _ o:OrderClause()? _ l:LimitOffsetClauses()? _ v:ValuesClause() {
QueryVariants::Ask {
dataset: d,
algebra: build_select(Selection::default(), w, g, h, o, l, v, state),
dataset: Rc::new(d),
algebra: Rc::new(build_select(Selection::default(), w, g, h, o, l, v, state)),
base_iri: state.base_iri.clone()
}
}

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

@ -6,6 +6,7 @@ use crate::store::numeric_encoder::{Encoder, ENCODED_DEFAULT_GRAPH};
use crate::Error;
use crate::Result;
use std::collections::{BTreeSet, HashSet};
use std::rc::Rc;
pub struct PlanBuilder<E: Encoder> {
encoder: E,
@ -39,8 +40,8 @@ impl<E: Encoder> PlanBuilder<E> {
Ok(match pattern {
GraphPattern::BGP(p) => self.build_for_bgp(p, variables, graph_name)?,
GraphPattern::Join(a, b) => PlanNode::Join {
left: Box::new(self.build_for_graph_pattern(a, variables, graph_name)?),
right: Box::new(self.build_for_graph_pattern(b, variables, graph_name)?),
left: Rc::new(self.build_for_graph_pattern(a, variables, graph_name)?),
right: Rc::new(self.build_for_graph_pattern(b, variables, graph_name)?),
},
GraphPattern::LeftJoin(a, b, e) => {
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
let right = if let Some(e) = e {
PlanNode::Filter {
child: Box::new(right),
expression: self.build_for_expression(e, variables, graph_name)?,
child: Rc::new(right),
expression: Rc::new(self.build_for_expression(e, variables, graph_name)?),
}
} else {
right
};
PlanNode::LeftJoin {
left: Box::new(left),
right: Box::new(right),
possible_problem_vars: possible_problem_vars.into_iter().collect(),
left: Rc::new(left),
right: Rc::new(right),
possible_problem_vars: Rc::new(possible_problem_vars.into_iter().collect()),
}
}
GraphPattern::Filter(e, p) => PlanNode::Filter {
child: Box::new(self.build_for_graph_pattern(p, variables, graph_name)?),
expression: self.build_for_expression(e, variables, graph_name)?,
child: Rc::new(self.build_for_graph_pattern(p, variables, graph_name)?),
expression: Rc::new(self.build_for_expression(e, variables, graph_name)?),
},
GraphPattern::Union(a, b) => {
//We flatten the UNIONs
@ -80,9 +81,9 @@ impl<E: Encoder> PlanBuilder<E> {
stack.push(a);
stack.push(b);
}
Some(p) => {
children.push(self.build_for_graph_pattern(p, variables, graph_name)?)
}
Some(p) => children.push(Rc::new(
self.build_for_graph_pattern(p, variables, graph_name)?,
)),
}
}
PlanNode::Union { children }
@ -92,13 +93,13 @@ impl<E: Encoder> PlanBuilder<E> {
self.build_for_graph_pattern(p, variables, graph_name)?
}
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),
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 {
left: Box::new(self.build_for_graph_pattern(a, variables, graph_name)?),
right: Box::new(self.build_for_graph_pattern(b, variables, graph_name)?),
left: Rc::new(self.build_for_graph_pattern(a, variables, graph_name)?),
right: Rc::new(self.build_for_graph_pattern(b, variables, graph_name)?),
},
GraphPattern::Service(n, p, s) => {
// 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)?;
PlanNode::Service {
service_name,
variables: variables.clone(),
child: Box::new(child),
graph_pattern: Box::new(*p.clone()),
variables: Rc::new(variables.clone()),
child: Rc::new(child),
graph_pattern: Rc::new(*p.clone()),
silent: *s,
}
}
@ -118,29 +119,32 @@ impl<E: Encoder> PlanBuilder<E> {
self.convert_pattern_value_id(graph_name, variables, &mut inner_variables);
PlanNode::Aggregate {
child: Box::new(self.build_for_graph_pattern(
child: Rc::new(self.build_for_graph_pattern(
p,
&mut inner_variables,
inner_graph_name,
)?),
key_mapping: key
.iter()
.map(|k| {
(
variable_key(&mut inner_variables, k),
variable_key(variables, k),
)
})
.collect(),
aggregates: aggregates
.iter()
.map(|(a, v)| {
Ok((
self.build_for_aggregate(a, &mut inner_variables, graph_name)?,
variable_key(variables, v),
))
})
.collect::<Result<Vec<_>>>()?,
key_mapping: Rc::new(
key.iter()
.map(|k| {
(
variable_key(&mut inner_variables, k),
variable_key(variables, k),
)
})
.collect(),
),
aggregates: Rc::new(
aggregates
.iter()
.map(|(a, v)| {
Ok((
self.build_for_aggregate(a, &mut inner_variables, graph_name)?,
variable_key(variables, v),
))
})
.collect::<Result<Vec<_>>>()?,
),
}
}
GraphPattern::Data(bs) => PlanNode::StaticBindings {
@ -159,7 +163,7 @@ impl<E: Encoder> PlanBuilder<E> {
})
.collect();
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?,
}
}
@ -168,35 +172,37 @@ impl<E: Encoder> PlanBuilder<E> {
let inner_graph_name =
self.convert_pattern_value_id(graph_name, variables, &mut inner_variables);
PlanNode::Project {
child: Box::new(self.build_for_graph_pattern(
child: Rc::new(self.build_for_graph_pattern(
l,
&mut inner_variables,
inner_graph_name,
)?),
mapping: new_variables
.iter()
.enumerate()
.map(|(new_variable, variable)| {
(new_variable, variable_key(variables, variable))
})
.collect(),
mapping: Rc::new(
new_variables
.iter()
.enumerate()
.map(|(new_variable, variable)| {
(new_variable, variable_key(variables, variable))
})
.collect(),
),
}
}
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::Slice(l, start, length) => {
let mut plan = self.build_for_graph_pattern(l, variables, graph_name)?;
if *start > 0 {
plan = PlanNode::Skip {
child: Box::new(plan),
child: Rc::new(plan),
count: *start,
};
}
if let Some(length) = length {
plan = PlanNode::Limit {
child: Box::new(plan),
child: Rc::new(plan),
count: *length,
};
}
@ -215,7 +221,7 @@ impl<E: Encoder> PlanBuilder<E> {
for pattern in sort_bgp(p) {
plan = match pattern {
TripleOrPathPattern::Triple(pattern) => PlanNode::QuadPatternJoin {
child: Box::new(plan),
child: Rc::new(plan),
subject: self
.pattern_value_from_term_or_variable(&pattern.subject, variables)?,
predicate: self
@ -224,10 +230,10 @@ impl<E: Encoder> PlanBuilder<E> {
graph_name,
},
TripleOrPathPattern::Path(pattern) => PlanNode::PathPatternJoin {
child: Box::new(plan),
child: Rc::new(plan),
subject: self
.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)?,
graph_name,
},
@ -242,30 +248,30 @@ impl<E: Encoder> PlanBuilder<E> {
self.encoder.encode_named_node(p).map_err(|e| e.into())?,
),
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(
Box::new(self.build_for_path(a)?),
Box::new(self.build_for_path(b)?),
Rc::new(self.build_for_path(a)?),
Rc::new(self.build_for_path(b)?),
),
PropertyPath::SequencePath(a, b) => PlanPropertyPath::SequencePath(
Box::new(self.build_for_path(a)?),
Box::new(self.build_for_path(b)?),
Rc::new(self.build_for_path(a)?),
Rc::new(self.build_for_path(b)?),
),
PropertyPath::ZeroOrMorePath(p) => {
PlanPropertyPath::ZeroOrMorePath(Box::new(self.build_for_path(p)?))
PlanPropertyPath::ZeroOrMorePath(Rc::new(self.build_for_path(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) => {
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()
.map(|p| self.encoder.encode_named_node(p).map_err(|e| e.into()))
.collect::<Result<Vec<_>>>()?,
),
)),
})
}
@ -678,7 +684,7 @@ impl<E: Encoder> PlanBuilder<E> {
}
},
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)?,
)),
})
@ -822,7 +828,7 @@ impl<E: Encoder> PlanBuilder<E> {
},
Aggregation::GroupConcat(e, distinct, separator) => PlanAggregation {
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)?),
distinct: *distinct,
@ -936,8 +942,8 @@ impl<E: Encoder> PlanBuilder<E> {
self.add_left_join_problematic_variables(&*child, set);
}
PlanNode::Union { children } => {
for child in children {
self.add_left_join_problematic_variables(&*child, set);
for child in children.iter() {
self.add_left_join_problematic_variables(child, set);
}
}
PlanNode::Join { left, right, .. } => {
@ -981,7 +987,7 @@ impl<E: Encoder> PlanBuilder<E> {
} => {
set.extend(key_mapping.iter().map(|(_, o)| o));
//TODO: This is too harsh
for (_, var) in aggregates {
for (_, var) in aggregates.iter() {
set.insert(*var);
}
}

@ -15,8 +15,9 @@ use std::collections::BTreeMap;
use std::io::BufRead;
use std::io::Write;
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);
match results {
QueryResult::Boolean(value) => {
@ -102,7 +103,7 @@ pub fn write_xml_results<W: Write>(results: QueryResult<'_>, sink: W) -> Result<
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 {
Start,
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);
}
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 {
reader,
buffer: Vec::default(),
@ -209,7 +210,7 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
State::AfterHead => {
if event.name() == b"results" {
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()),
)))
} else {

@ -2,7 +2,7 @@
use crate::error::UnwrapInfallible;
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::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore};
use crate::{DatasetSyntax, Error, GraphSyntax};
@ -39,8 +39,7 @@ use std::vec::IntoIter;
/// assert_eq!(vec![quad], results);
///
/// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? {
/// 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(())
@ -82,7 +81,36 @@ impl MemoryStore {
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.
/// It is useful if you want to execute multiple times the same SPARQL query.
///
/// 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
///
/// Usage example:
@ -863,7 +878,7 @@ pub struct MemoryPreparedQuery(SimplePreparedQuery<MemoryStore>);
impl MemoryPreparedQuery {
/// Evaluates the query and returns its results
pub fn exec(&self) -> crate::Result<QueryResult<'_>> {
pub fn exec(&self) -> crate::Result<QueryResult> {
self.0.exec()
}
}

@ -2,7 +2,7 @@
use crate::error::UnwrapInfallible;
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::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore};
use crate::{DatasetSyntax, GraphSyntax, Result};
@ -39,9 +39,7 @@ use std::{fmt, str};
/// assert_eq!(vec![quad], results?);
///
/// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// let results = prepared_query.exec()?;
/// if let QueryResult::Solutions(mut solutions) = results {
/// 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()));
/// }
/// #
@ -89,30 +87,29 @@ impl RocksDbStore {
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.
pub fn prepare_query(
pub fn query(
&self,
query: impl TryInto<Query, Error = impl Into<crate::Error>>,
options: QueryOptions,
) -> Result<RocksDbPreparedQuery> {
Ok(RocksDbPreparedQuery(SimplePreparedQuery::new(
(*self).clone(),
query,
options,
)?))
) -> Result<QueryResult> {
self.prepare_query(query, options)?.exec()
}
/// 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(
/// 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.
///
/// See `MemoryStore` for a usage example.
pub fn prepare_query(
&self,
graph_pattern: &GraphPattern,
query: impl TryInto<Query, Error = impl Into<crate::Error>>,
options: QueryOptions,
) -> Result<RocksDbPreparedQuery> {
Ok(RocksDbPreparedQuery(SimplePreparedQuery::new_from_pattern(
Ok(RocksDbPreparedQuery(SimplePreparedQuery::new(
(*self).clone(),
graph_pattern,
query,
options,
)?))
}
@ -511,7 +508,7 @@ pub struct RocksDbPreparedQuery(SimplePreparedQuery<RocksDbStore>);
impl RocksDbPreparedQuery {
/// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult<'_>> {
pub fn exec(&self) -> Result<QueryResult> {
self.0.exec()
}
}

@ -2,7 +2,7 @@
use crate::error::UnwrapInfallible;
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::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore};
use crate::{DatasetSyntax, Error, GraphSyntax, Result};
@ -39,8 +39,7 @@ use std::{fmt, str};
/// assert_eq!(vec![quad], results?);
///
/// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? {
/// 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()));
/// };
/// #
@ -84,30 +83,28 @@ impl SledStore {
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.
pub fn prepare_query(
pub fn query(
&self,
query: impl TryInto<Query, Error = impl Into<Error>>,
options: QueryOptions,
) -> Result<SledPreparedQuery> {
Ok(SledPreparedQuery(SimplePreparedQuery::new(
(*self).clone(),
query,
options,
)?))
) -> Result<QueryResult> {
self.prepare_query(query, options)?.exec()
}
/// 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(
/// Prepares a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) and returns an object that could be used to execute it.
///
/// See `MemoryStore` for a usage example.
pub fn prepare_query(
&self,
graph_pattern: &GraphPattern,
query: impl TryInto<Query, Error = impl Into<Error>>,
options: QueryOptions,
) -> Result<SledPreparedQuery> {
Ok(SledPreparedQuery(SimplePreparedQuery::new_from_pattern(
Ok(SledPreparedQuery(SimplePreparedQuery::new(
(*self).clone(),
graph_pattern,
query,
options,
)?))
}
@ -670,7 +667,7 @@ pub struct SledPreparedQuery(SimplePreparedQuery<SledStore>);
impl SledPreparedQuery {
/// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult<'_>> {
pub fn exec(&self) -> Result<QueryResult> {
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::{DatasetSyntax, FileSyntax, GraphSyntax, MemoryStore};
use pyo3::basic::CompareOp;
use pyo3::exceptions::{NotImplementedError, RuntimeError, ValueError};
use pyo3::exceptions::{NotImplementedError, ValueError};
use pyo3::prelude::*;
use pyo3::types::PyTuple;
use pyo3::{PyIterProtocol, PyObjectProtocol, PySequenceProtocol};
@ -55,14 +55,11 @@ impl PyMemoryStore {
}
fn query(&self, query: &str, py: Python<'_>) -> PyResult<PyObject> {
let query = self
let results = self
.inner
.prepare_query(query, QueryOptions::default())
.query(query, QueryOptions::default())
.map_err(|e| ParseError::py_err(e.to_string()))?;
let results = query
.exec()
.map_err(|e| RuntimeError::py_err(e.to_string()))?;
query_results_to_python(py, results, RuntimeError::py_err)
query_results_to_python(py, results)
}
#[args(data, mime_type, "*", base_iri = "\"\"", to_graph = "None")]

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

@ -1,12 +1,11 @@
use crate::model::*;
use oxigraph::model::*;
use oxigraph::sparql::{QueryResult, QuerySolution};
use oxigraph::sparql::{QueryResult, QuerySolution, QuerySolutionsIterator};
use oxigraph::Result;
use pyo3::exceptions::TypeError;
use pyo3::exceptions::{IOError, TypeError};
use pyo3::prelude::*;
use pyo3::{create_exception, PyIterProtocol, PyMappingProtocol, PyNativeType, PyObjectProtocol};
use std::fmt::Write;
use std::vec::IntoIter;
create_exception!(oxigraph, ParseError, pyo3::exceptions::Exception);
@ -49,26 +48,10 @@ pub fn extract_quads_pattern(
))
}
pub fn query_results_to_python(
py: Python<'_>,
results: QueryResult<'_>,
error: impl Fn(String) -> PyErr,
) -> PyResult<PyObject> {
pub fn query_results_to_python(py: Python<'_>, results: QueryResult) -> PyResult<PyObject> {
Ok(match results {
QueryResult::Solutions(solutions) => QuerySolutionIter {
inner: solutions
.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::Solutions(inner) => QuerySolutionIter { inner }.into_py(py),
QueryResult::Graph(inner) => TripleResultIter { inner }.into_py(py),
QueryResult::Boolean(b) => b.into_py(py),
})
}
@ -118,7 +101,7 @@ impl PyMappingProtocol for PyQuerySolution {
#[pyclass(unsendable)]
pub struct QuerySolutionIter {
inner: IntoIter<QuerySolution>,
inner: QuerySolutionsIterator,
}
#[pyproto]
@ -127,14 +110,19 @@ impl PyIterProtocol for QuerySolutionIter {
slf.into()
}
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyQuerySolution> {
slf.inner.next().map(move |inner| PyQuerySolution { inner })
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<PyQuerySolution>> {
Ok(slf
.inner
.next()
.transpose()
.map_err(|e| IOError::py_err(e.to_string()))? //TODO: improve
.map(move |inner| PyQuerySolution { inner }))
}
}
#[pyclass(unsendable)]
pub struct TripleResultIter {
inner: IntoIter<Triple>,
inner: Box<dyn Iterator<Item = Result<Triple>>>,
}
#[pyproto]
@ -143,7 +131,12 @@ impl PyIterProtocol for TripleResultIter {
slf.into()
}
fn __next__(mut slf: PyRefMut<Self>) -> Option<(PyObject, PyObject, PyObject)> {
slf.inner.next().map(move |t| triple_to_python(slf.py(), t))
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<(PyObject, PyObject, PyObject)>> {
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 http_types::{headers, Body, Error, Method, Mime, Request, Response, Result, StatusCode};
use oxigraph::model::GraphName;
use oxigraph::sparql::{QueryOptions, QueryResult, QueryResultSyntax};
use oxigraph::sparql::{Query, QueryOptions, QueryResult, QueryResultSyntax};
use oxigraph::{DatasetSyntax, FileSyntax, GraphSyntax, RocksDbStore};
use std::str::FromStr;
use url::form_urlencoded;
@ -163,15 +163,13 @@ async fn evaluate_sparql_query(
request: Request,
) -> Result<Response> {
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
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 {
let format = content_negotiation(
request,

@ -84,15 +84,12 @@ fn evaluate_sparql_test(test: &Test) -> Result<()> {
.ok_or_else(|| Error::msg(format!("No action found for test {}", test)))?;
let options = QueryOptions::default()
.with_service_handler(StaticServiceHandler::new(&test.service_data)?);
match store.prepare_query(
Query::parse(&read_file_to_string(query_file)?, Some(query_file))?,
options,
) {
match Query::parse(&read_file_to_string(query_file)?, Some(query_file)) {
Err(error) => Err(Error::msg(format!(
"Failure to parse query of {} with error: {}",
test, error
))),
Ok(query) => match query.exec() {
Ok(query) => match store.query(query, options) {
Err(error) => Err(Error::msg(format!(
"Failure to execute query of {} with error: {}",
test, error
@ -173,35 +170,18 @@ impl StaticServiceHandler {
}
impl ServiceHandler for StaticServiceHandler {
fn handle<'a>(
&'a self,
service_name: &NamedNode,
graph_pattern: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
if let QueryResult::Solutions(iterator) = self
.services
.get(service_name)
fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResult> {
self.services
.get(&service_name)
.ok_or_else(|| Error::msg(format!("Service {} not found", service_name)))?
.prepare_query_from_pattern(
&graph_pattern,
.query(
query,
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 {
QueryResult::Graph(graph) => graph.map(|t| t.map(|t| t.in_graph(None))).collect(),
QueryResult::Boolean(value) => {
@ -377,10 +357,7 @@ impl fmt::Display for StaticQueryResults {
}
impl StaticQueryResults {
fn from_query_results(
results: QueryResult<'_>,
with_order: bool,
) -> Result<StaticQueryResults> {
fn from_query_results(results: QueryResult, with_order: bool) -> Result<StaticQueryResults> {
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::task::{spawn, spawn_blocking};
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 std::str::FromStr;
use std::time::Duration;
@ -174,14 +174,12 @@ async fn evaluate_sparql_query(
) -> Result<Response> {
spawn_blocking(move || {
//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()?;
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())?;
if let QueryResult::Graph(_) = results {
let format = content_negotiation(
request,

Loading…
Cancel
Save