diff --git a/lib/src/repository.rs b/lib/src/repository.rs index 273f7f23..11c35fa8 100644 --- a/lib/src/repository.rs +++ b/lib/src/repository.rs @@ -1,6 +1,7 @@ use crate::model::*; use crate::sparql::{GraphPattern, PreparedQuery, QueryOptions}; use crate::{DatasetSyntax, GraphSyntax, Result}; +use rio_api::iri::Iri; use std::io::BufRead; /// A `Repository` stores a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) @@ -81,7 +82,13 @@ pub trait RepositoryConnection: Clone { /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// } /// ``` - fn prepare_query<'a>(&'a self, query: &str, options: &'a QueryOptions<'a>) -> Result; + fn prepare_query<'a>(&'a self, query: &str, base_iri: Option<&'a str>) -> Result; + + + fn prepare_query_from_pattern<'a>( + &'a self, + graph_pattern: &'a GraphPattern, + ) -> Result; /// Retrieves quads with a filter on each quad component /// @@ -112,13 +119,6 @@ pub trait RepositoryConnection: Clone { where Self: 'a; - fn prepare_query_from_pattern<'a>( - &'a self, - graph_pattern: &'a GraphPattern, - options: &'a QueryOptions<'a> - ) -> Result; - - /// Loads a graph file (i.e. triples) into the repository /// /// Usage example: diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index e37fbfc5..b85a2386 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -41,16 +41,14 @@ type EncodedTuplesIterator<'a> = Box> + pub struct SimpleEvaluator { dataset: DatasetView, bnodes_map: Mutex>, - base_iri: Option>, now: DateTime, } impl<'a, S: StoreConnection + 'a> SimpleEvaluator { - pub fn new(dataset: DatasetView, base_iri: Option>) -> Self { + pub fn new(dataset: DatasetView) -> Self { Self { dataset, bnodes_map: Mutex::new(BTreeMap::default()), - base_iri, now: Utc::now().with_timezone(&FixedOffset::east(0)), } } @@ -113,6 +111,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { { Ok(QueryResult::Graph(Box::new(DescribeIterator { eval: self, + options, iter: self.eval_plan(plan, vec![], options), quads: Box::new(empty()), }))) @@ -198,6 +197,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { get_pattern_value(&predicate, &tuple), get_pattern_value(&object, &tuple), get_pattern_value(&graph_name, &tuple), + options.default_graph_as_union ); if subject.is_var() && subject == predicate { iter = Box::new(iter.filter(|quad| match quad { @@ -271,7 +271,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { }; match (input_subject, input_object) { (Some(input_subject), Some(input_object)) => Box::new( - self.eval_path_from(path, input_subject, input_graph_name) + self.eval_path_from(path, input_subject, input_graph_name, options) .filter_map(move |o| match o { Ok(o) => { if o == input_object { @@ -285,7 +285,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { ) as EncodedTuplesIterator<'_>, (Some(input_subject), None) => Box::new( - self.eval_path_from(path, input_subject, input_graph_name) + self.eval_path_from(path, input_subject, input_graph_name, options) .map(move |o| { let mut new_tuple = tuple.clone(); put_pattern_value(&object, o?, &mut new_tuple); @@ -293,7 +293,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { }), ), (None, Some(input_object)) => Box::new( - self.eval_path_to(path, input_object, input_graph_name) + self.eval_path_to(path, input_object, input_graph_name, options) .map(move |s| { let mut new_tuple = tuple.clone(); put_pattern_value(&subject, s?, &mut new_tuple); @@ -301,7 +301,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { }), ), (None, None) => { - Box::new(self.eval_open_path(path, input_graph_name).map(move |so| { + Box::new(self.eval_open_path(path, input_graph_name, options).map(move |so| { let mut new_tuple = tuple.clone(); so.map(move |(s, o)| { put_pattern_value(&subject, s, &mut new_tuple); @@ -580,6 +580,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { path: &'b PlanPropertyPath, start: EncodedTerm, graph_name: EncodedTerm, + options: &'b QueryOptions<'b> ) -> Box> + 'b> where 'a: 'b, @@ -587,33 +588,33 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { match path { PlanPropertyPath::PredicatePath(p) => Box::new( self.dataset - .quads_for_pattern(Some(start), Some(*p), None, Some(graph_name)) + .quads_for_pattern(Some(start), Some(*p), None, Some(graph_name), options.default_graph_as_union) .map(|t| Ok(t?.object)), ), - PlanPropertyPath::InversePath(p) => self.eval_path_to(&p, start, graph_name), + PlanPropertyPath::InversePath(p) => self.eval_path_to(&p, start, graph_name, options), PlanPropertyPath::SequencePath(a, b) => Box::new( - self.eval_path_from(&a, start, graph_name) - .flat_map_ok(move |middle| self.eval_path_from(&b, middle, graph_name)), + self.eval_path_from(&a, start, graph_name, options) + .flat_map_ok(move |middle| self.eval_path_from(&b, middle, graph_name, options)), ), PlanPropertyPath::AlternativePath(a, b) => Box::new( - self.eval_path_from(&a, start, graph_name) - .chain(self.eval_path_from(&b, start, graph_name)), + self.eval_path_from(&a, start, graph_name, options) + .chain(self.eval_path_from(&b, start, graph_name, options)), ), PlanPropertyPath::ZeroOrMorePath(p) => { Box::new(transitive_closure(Some(Ok(start)), move |e| { - self.eval_path_from(p, e, graph_name) + self.eval_path_from(p, e, graph_name, options) })) } PlanPropertyPath::OneOrMorePath(p) => Box::new(transitive_closure( - self.eval_path_from(p, start, graph_name), - move |e| self.eval_path_from(p, e, graph_name), + self.eval_path_from(p, start, graph_name, options), + move |e| self.eval_path_from(p, e, graph_name, options), )), PlanPropertyPath::ZeroOrOnePath(p) => Box::new(hash_deduplicate( - once(Ok(start)).chain(self.eval_path_from(&p, start, graph_name)), + once(Ok(start)).chain(self.eval_path_from(&p, start, graph_name, options)), )), PlanPropertyPath::NegatedPropertySet(ps) => Box::new( self.dataset - .quads_for_pattern(Some(start), None, None, Some(graph_name)) + .quads_for_pattern(Some(start), None, None, Some(graph_name), options.default_graph_as_union) .filter(move |t| match t { Ok(t) => !ps.contains(&t.predicate), Err(_) => true, @@ -628,6 +629,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { path: &'b PlanPropertyPath, end: EncodedTerm, graph_name: EncodedTerm, + options: &'a QueryOptions<'b> ) -> Box> + 'b> where 'a: 'b, @@ -635,33 +637,33 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { match path { PlanPropertyPath::PredicatePath(p) => Box::new( self.dataset - .quads_for_pattern(None, Some(*p), Some(end), Some(graph_name)) + .quads_for_pattern(None, Some(*p), Some(end), Some(graph_name), options.default_graph_as_union) .map(|t| Ok(t?.subject)), ), - PlanPropertyPath::InversePath(p) => self.eval_path_from(&p, end, graph_name), + PlanPropertyPath::InversePath(p) => self.eval_path_from(&p, end, graph_name, options), PlanPropertyPath::SequencePath(a, b) => Box::new( - self.eval_path_to(&b, end, graph_name) - .flat_map_ok(move |middle| self.eval_path_to(&a, middle, graph_name)), + self.eval_path_to(&b, end, graph_name, options) + .flat_map_ok(move |middle| self.eval_path_to(&a, middle, graph_name, options)), ), PlanPropertyPath::AlternativePath(a, b) => Box::new( - self.eval_path_to(&a, end, graph_name) - .chain(self.eval_path_to(&b, end, graph_name)), + self.eval_path_to(&a, end, graph_name, options) + .chain(self.eval_path_to(&b, end, graph_name, options)), ), PlanPropertyPath::ZeroOrMorePath(p) => { Box::new(transitive_closure(Some(Ok(end)), move |e| { - self.eval_path_to(p, e, graph_name) + self.eval_path_to(p, e, graph_name, options) })) } PlanPropertyPath::OneOrMorePath(p) => Box::new(transitive_closure( - self.eval_path_to(p, end, graph_name), - move |e| self.eval_path_to(p, e, graph_name), + self.eval_path_to(p, end, graph_name, options), + move |e| self.eval_path_to(p, e, graph_name, options), )), PlanPropertyPath::ZeroOrOnePath(p) => Box::new(hash_deduplicate( - once(Ok(end)).chain(self.eval_path_to(&p, end, graph_name)), + once(Ok(end)).chain(self.eval_path_to(&p, end, graph_name, options)), )), PlanPropertyPath::NegatedPropertySet(ps) => Box::new( self.dataset - .quads_for_pattern(None, None, Some(end), Some(graph_name)) + .quads_for_pattern(None, None, Some(end), Some(graph_name), options.default_graph_as_union) .filter(move |t| match t { Ok(t) => !ps.contains(&t.predicate), Err(_) => true, @@ -675,6 +677,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { &'b self, path: &'b PlanPropertyPath, graph_name: EncodedTerm, + options: &'b QueryOptions<'b> ) -> Box> + 'b> where 'a: 'b, @@ -682,45 +685,45 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { match path { PlanPropertyPath::PredicatePath(p) => Box::new( self.dataset - .quads_for_pattern(None, Some(*p), None, Some(graph_name)) + .quads_for_pattern(None, Some(*p), None, Some(graph_name), options.default_graph_as_union) .map(|t| t.map(|t| (t.subject, t.object))), ), PlanPropertyPath::InversePath(p) => Box::new( - self.eval_open_path(&p, graph_name) + self.eval_open_path(&p, graph_name, options) .map(|t| t.map(|(s, o)| (o, s))), ), PlanPropertyPath::SequencePath(a, b) => Box::new( - self.eval_open_path(&a, graph_name) + self.eval_open_path(&a, graph_name, options) .flat_map_ok(move |(start, middle)| { - self.eval_path_from(&b, middle, graph_name) + self.eval_path_from(&b, middle, graph_name, options) .map(move |end| Ok((start, end?))) }), ), PlanPropertyPath::AlternativePath(a, b) => Box::new( - self.eval_open_path(&a, graph_name) - .chain(self.eval_open_path(&b, graph_name)), + self.eval_open_path(&a, graph_name, options) + .chain(self.eval_open_path(&b, graph_name, options)), ), PlanPropertyPath::ZeroOrMorePath(p) => Box::new(transitive_closure( - self.get_subject_or_object_identity_pairs(graph_name), //TODO: avoid to inject everything + self.get_subject_or_object_identity_pairs(graph_name, options), //TODO: avoid to inject everything move |(start, middle)| { - self.eval_path_from(p, middle, graph_name) + self.eval_path_from(p, middle, graph_name, options) .map(move |end| Ok((start, end?))) }, )), PlanPropertyPath::OneOrMorePath(p) => Box::new(transitive_closure( - self.eval_open_path(p, graph_name), + self.eval_open_path(p, graph_name, options), move |(start, middle)| { - self.eval_path_from(p, middle, graph_name) + self.eval_path_from(p, middle, graph_name, options) .map(move |end| Ok((start, end?))) }, )), PlanPropertyPath::ZeroOrOnePath(p) => Box::new(hash_deduplicate( - self.get_subject_or_object_identity_pairs(graph_name) - .chain(self.eval_open_path(&p, graph_name)), + self.get_subject_or_object_identity_pairs(graph_name, options) + .chain(self.eval_open_path(&p, graph_name, options)), )), PlanPropertyPath::NegatedPropertySet(ps) => Box::new( self.dataset - .quads_for_pattern(None, None, None, Some(graph_name)) + .quads_for_pattern(None, None, None, Some(graph_name), options.default_graph_as_union) .filter(move |t| match t { Ok(t) => !ps.contains(&t.predicate), Err(_) => true, @@ -733,9 +736,10 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { fn get_subject_or_object_identity_pairs<'b>( &'b self, graph_name: EncodedTerm, + options: &'b QueryOptions<'b> ) -> impl Iterator> + 'b { self.dataset - .quads_for_pattern(None, None, None, Some(graph_name)) + .quads_for_pattern(None, None, None, Some(graph_name), options.default_graph_as_union) .flat_map_ok(|t| once(Ok(t.subject)).chain(once(Ok(t.object)))) .map(|e| e.map(|e| (e, e))) } @@ -929,7 +933,8 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { _ => None, }?; let iri = self.dataset.get_str(iri_id).ok()??; - if let Some(base_iri) = &self.base_iri { + let base_iri = options.base_iri.map(|base_iri| Iri::parse(base_iri)); + if let Some(Ok(base_iri)) = base_iri { self.build_named_node(&base_iri.resolve(&iri).ok()?.into_inner()) } else { Iri::parse(iri).ok()?; @@ -2350,6 +2355,7 @@ fn decode_triple( struct DescribeIterator<'a, S: StoreConnection + 'a> { eval: &'a SimpleEvaluator, + options: &'a QueryOptions<'a>, iter: EncodedTuplesIterator<'a>, quads: Box> + 'a>, } @@ -2378,7 +2384,7 @@ impl<'a, S: StoreConnection + 'a> Iterator for DescribeIterator<'a, S> { self.quads = self.eval .dataset - .quads_for_pattern(Some(subject), None, None, None); + .quads_for_pattern(Some(subject), None, None, None, self.options.default_graph_as_union); } } } diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index 491acb99..fa8eae91 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -19,7 +19,6 @@ use crate::sparql::plan_builder::PlanBuilder; use crate::store::StoreConnection; use crate::Result; use std::fmt; -use rio_api::iri::Iri; pub use crate::sparql::algebra::GraphPattern; pub use crate::sparql::model::BindingsIterator; @@ -58,10 +57,10 @@ enum SimplePreparedQueryAction { } impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { - pub(crate) fn new(connection: S, query: &str, options: &'a QueryOptions<'a>) -> Result { - let dataset = DatasetView::new(connection, options.default_graph_as_union); + pub(crate) fn new(connection: S, query: &str, base_iri: Option<&'a str>) -> Result { + let dataset = DatasetView::new(connection); //TODO avoid inserting terms in the Repository StringStore - Ok(Self(match read_sparql_query(query, options.base_iri)? { + Ok(Self(match read_sparql_query(query, base_iri)? { QueryVariants::Select { algebra, dataset: _, @@ -71,7 +70,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { SimplePreparedQueryAction::Select { plan, variables, - evaluator: SimpleEvaluator::new(dataset, base_iri), + evaluator: SimpleEvaluator::new(dataset), } } QueryVariants::Ask { @@ -82,7 +81,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Ask { plan, - evaluator: SimpleEvaluator::new(dataset, base_iri), + evaluator: SimpleEvaluator::new(dataset), } } QueryVariants::Construct { @@ -99,7 +98,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { &construct, variables, )?, - evaluator: SimpleEvaluator::new(dataset, base_iri), + evaluator: SimpleEvaluator::new(dataset), } } QueryVariants::Describe { @@ -110,7 +109,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Describe { plan, - evaluator: SimpleEvaluator::new(dataset, base_iri), + evaluator: SimpleEvaluator::new(dataset), } } })) @@ -119,15 +118,13 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { pub(crate) fn new_from_pattern( connection: S, pattern: &GraphPattern, - options: &'a QueryOptions<'a> ) -> Result { - let dataset = DatasetView::new(connection, options.default_graph_as_union); - let iri = options.base_iri.map(|i| Iri::parse(i.to_string()).unwrap()); + let dataset = DatasetView::new(connection); let (plan, variables) = PlanBuilder::build(dataset.encoder(), pattern)?; Ok(Self(SimplePreparedQueryAction::Select { plan, variables, - evaluator: SimpleEvaluator::new(dataset, iri), + evaluator: SimpleEvaluator::new(dataset), })) } } diff --git a/lib/src/sparql/plan.rs b/lib/src/sparql/plan.rs index 11246f1e..9b84a8aa 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -473,15 +473,13 @@ pub enum TripleTemplateValue { pub struct DatasetView { store: S, extra: RefCell, - default_graph_as_union: bool, } impl DatasetView { - pub fn new(store: S, default_graph_as_union: bool) -> Self { + pub fn new(store: S) -> Self { Self { store, extra: RefCell::new(MemoryStrStore::default()), - default_graph_as_union, } } @@ -491,6 +489,7 @@ impl DatasetView { predicate: Option, object: Option, graph_name: Option, + default_graph_as_union: bool, ) -> Box> + 'a> { if graph_name == None { Box::new( @@ -501,7 +500,7 @@ impl DatasetView { Ok(quad) => quad.graph_name != ENCODED_DEFAULT_GRAPH, }), ) - } else if graph_name == Some(ENCODED_DEFAULT_GRAPH) && self.default_graph_as_union { + } else if graph_name == Some(ENCODED_DEFAULT_GRAPH) && default_graph_as_union { Box::new( self.store .quads_for_pattern(subject, predicate, object, None) diff --git a/lib/src/store/mod.rs b/lib/src/store/mod.rs index 03477db7..d27d04e8 100644 --- a/lib/src/store/mod.rs +++ b/lib/src/store/mod.rs @@ -11,9 +11,10 @@ pub use crate::store::memory::MemoryRepository; pub use crate::store::rocksdb::RocksDbRepository; use crate::model::*; -use crate::sparql::{QueryOptions, SimplePreparedQuery}; +use crate::sparql::{SimplePreparedQuery}; use crate::store::numeric_encoder::*; use crate::{DatasetSyntax, GraphSyntax, RepositoryConnection, Result}; +use rio_api::iri::Iri; use rio_api::parser::{QuadsParser, TriplesParser}; use rio_turtle::{NQuadsParser, NTriplesParser, TriGParser, TurtleParser}; use rio_xml::RdfXmlParser; @@ -72,8 +73,8 @@ impl From for StoreRepositoryConnection { impl RepositoryConnection for StoreRepositoryConnection { type PreparedQuery = SimplePreparedQuery; - fn prepare_query<'a>(&self, query: &str, options: &'a QueryOptions<'a>) -> Result> { - SimplePreparedQuery::new(self.inner.clone(), query, options) //TODO: avoid clone + fn prepare_query<'a>(&self, query: &str, base_iri: Option<&'a str>) -> Result> { + SimplePreparedQuery::new(self.inner.clone(), query, base_iri) //TODO: avoid clone } fn quads_for_pattern<'a>( @@ -101,13 +102,10 @@ impl RepositoryConnection for StoreRepositoryConnection { fn prepare_query_from_pattern<'a>( &'a self, pattern: &GraphPattern, - options: &'a QueryOptions<'a> ) -> Result { - SimplePreparedQuery::new_from_pattern(self.inner.clone(), pattern, options) //TODO: avoid clone + SimplePreparedQuery::new_from_pattern(self.inner.clone(), pattern) //TODO: avoid clone } - - fn load_graph( &mut self, reader: impl BufRead, diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs index 6a3d6259..3016a013 100644 --- a/lib/tests/service_test_cases.rs +++ b/lib/tests/service_test_cases.rs @@ -110,7 +110,7 @@ fn silent_service_test() { #[derive(Clone,Copy)] struct TwoServiceTest; impl ServiceHandler for TwoServiceTest { - fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { + fn handle<'a>(&'a self, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { Some(TwoServiceTest::handle_service) } } @@ -174,7 +174,7 @@ fn make_repository(reader: impl BufRead) -> Result { fn query_repository<'a>(repository: MemoryRepository, query: String, options: QueryOptions<'a>) -> Result> { let connection = repository.connection()?; - let prepared_query = connection.prepare_query(&query, &options)?; + let prepared_query = connection.prepare_query(&query, None)?; let result = prepared_query.exec(&options)?; match result { QueryResult::Bindings(iterator) => { @@ -186,10 +186,9 @@ fn query_repository<'a>(repository: MemoryRepository, query: String, options: Qu } } -//fn pattern_repository<'a>(repository: MemoryRepository, pattern: GraphPattern, options: QueryOptions<'a>) -> Result>>> { fn pattern_repository<'a>(repository: MemoryRepository, pattern: GraphPattern, options: QueryOptions<'a>) -> Result> { let connection = repository.connection()?; - let prepared_query = connection.prepare_query_from_pattern(&pattern, &options)?; + let prepared_query = connection.prepare_query_from_pattern(&pattern)?; let result = prepared_query.exec(&options)?; match result { QueryResult::Bindings(iterator) => { @@ -201,13 +200,11 @@ fn pattern_repository<'a>(repository: MemoryRepository, pattern: GraphPattern, o } } -//fn do_query<'a>(reader: impl BufRead, query: String, options: QueryOptions<'a>) -> Result>>> { fn do_query<'a>(reader: impl BufRead, query: String, options: QueryOptions<'a>) -> Result> { let repository = make_repository(reader)?; query_repository(repository, query, options) } -//fn do_pattern<'a>(reader: impl BufRead, pattern: GraphPattern, options: QueryOptions<'a>) -> Result>>> { fn do_pattern<'a>(reader: impl BufRead, pattern: GraphPattern, options: QueryOptions<'a>) -> Result> { let repository = make_repository(reader)?; pattern_repository(repository, pattern, options) diff --git a/lib/tests/sparql_test_cases.rs b/lib/tests/sparql_test_cases.rs index ea802e60..5555df9e 100644 --- a/lib/tests/sparql_test_cases.rs +++ b/lib/tests/sparql_test_cases.rs @@ -158,7 +158,7 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { } match repository .connection()? - .prepare_query(&read_file_to_string(&test.query)?, &QueryOptions::default().with_base_iri(&test.query)) + .prepare_query(&read_file_to_string(&test.query)?, None) { Err(error) => Err(format_err!( "Failure to parse query of {} with error: {}",