diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 69dba29e..a5e53098 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -27,8 +27,9 @@ //! assert_eq!(vec![quad], results.unwrap()); //! //! // SPARQL query -//! let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); -//! let results = prepared_query.exec().unwrap(); +//! let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap(); +//! let options = QueryOptions::default(); +//! let results = prepared_query.exec(&options).unwrap(); //! if let QueryResult::Bindings(results) = results { //! assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); //! } diff --git a/lib/src/repository.rs b/lib/src/repository.rs index 7d1aca4d..2ee0ad10 100644 --- a/lib/src/repository.rs +++ b/lib/src/repository.rs @@ -1,5 +1,5 @@ use crate::model::*; -use crate::sparql::{PreparedQuery, QueryOptions}; +use crate::sparql::{GraphPattern, PreparedQuery}; use crate::{DatasetSyntax, GraphSyntax, Result}; use std::io::BufRead; @@ -30,8 +30,9 @@ use std::io::BufRead; /// assert_eq!(vec![quad], results.unwrap()); /// /// // SPARQL query -/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); -/// let results = prepared_query.exec().unwrap(); +/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap(); +/// let options = QueryOptions::default(); +/// let results = prepared_query.exec(&options).unwrap(); /// if let QueryResult::Bindings(results) = results { /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// } @@ -75,13 +76,21 @@ pub trait RepositoryConnection: Clone { /// connection.insert(&Quad::new(ex.clone(), ex.clone(), ex.clone(), None)); /// /// // SPARQL query - /// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); - /// let results = prepared_query.exec().unwrap(); + /// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap(); + /// let options = QueryOptions::default(); + /// let results = prepared_query.exec(&options).unwrap(); /// if let QueryResult::Bindings(results) = results { /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// } /// ``` - fn prepare_query(&self, query: &str, options: QueryOptions) -> Result; + fn prepare_query<'a>(&'a self, query: &str, base_iri: Option<&'a str>) -> Result; + + /// 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. + fn prepare_query_from_pattern<'a>( + &'a self, + graph_pattern: &'a GraphPattern, + base_iri: Option<&str>, + ) -> Result; /// Retrieves quads with a filter on each quad component /// diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index c75d12cc..989dcb90 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -1,6 +1,7 @@ use crate::model::BlankNode; use crate::model::Triple; use crate::sparql::model::*; +use crate::sparql::QueryOptions; use crate::sparql::plan::*; use crate::store::numeric_encoder::*; use crate::store::StoreConnection; @@ -39,8 +40,8 @@ type EncodedTuplesIterator<'a> = Box> + pub struct SimpleEvaluator { dataset: DatasetView, - bnodes_map: Mutex>, base_iri: Option>, + bnodes_map: Mutex>, now: DateTime, } @@ -58,21 +59,26 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { &'b self, plan: &'b PlanNode, variables: &[Variable], + options: &'b QueryOptions<'b> ) -> Result> where 'a: 'b, { - let iter = self.eval_plan(plan, vec![None; variables.len()]); + let iter = self.eval_plan(plan, vec![None; variables.len()], &options); Ok(QueryResult::Bindings( self.decode_bindings(iter, variables.to_vec()), )) } - pub fn evaluate_ask_plan<'b>(&'b self, plan: &'b PlanNode) -> Result> + pub fn evaluate_ask_plan<'b>( + &'b self, + plan: &'b PlanNode, + options: &'b QueryOptions<'b> + ) -> Result> where 'a: 'b, { - match self.eval_plan(plan, vec![]).next() { + match self.eval_plan(plan, vec![], &options).next() { Some(Ok(_)) => Ok(QueryResult::Boolean(true)), Some(Err(error)) => Err(error), None => Ok(QueryResult::Boolean(false)), @@ -83,49 +89,132 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { &'b self, plan: &'b PlanNode, construct: &'b [TripleTemplate], + options: &'b QueryOptions<'b> ) -> Result> where 'a: 'b, { Ok(QueryResult::Graph(Box::new(ConstructIterator { eval: self, - iter: self.eval_plan(plan, vec![]), + iter: self.eval_plan(plan, vec![], options), template: construct, buffered_results: Vec::default(), bnodes: Vec::default(), }))) } - pub fn evaluate_describe_plan<'b>(&'b self, plan: &'b PlanNode) -> Result> + pub fn evaluate_describe_plan<'b>( + &'b self, + plan: &'b PlanNode, + options: &'b QueryOptions<'b> + ) -> Result> where 'a: 'b, { Ok(QueryResult::Graph(Box::new(DescribeIterator { eval: self, - iter: self.eval_plan(plan, vec![]), + options, + iter: self.eval_plan(plan, vec![], options), quads: Box::new(empty()), }))) } - fn eval_plan<'b>(&'b self, node: &'b PlanNode, from: EncodedTuple) -> EncodedTuplesIterator<'b> + fn eval_plan<'b>( + &'b self, + node: &'b PlanNode, + from: EncodedTuple, + options: &'b QueryOptions<'b> + ) -> EncodedTuplesIterator<'b> where 'a: 'b, { match node { PlanNode::Init => Box::new(once(Ok(from))), PlanNode::StaticBindings { tuples } => Box::new(tuples.iter().cloned().map(Ok)), + PlanNode::Service { + variables, + silent, + service_name, + graph_pattern, + .. + } => { + match &options.service_handler { + None => if *silent { + return Box::new(empty()); + } else { + return Box::new(once(Err(format_err!( + "No handler was supplied to resolve the given service" + )))) as EncodedTuplesIterator<'_>; + }, + Some(handler) => { + let pattern_option = match get_pattern_value(service_name, &[]) { + None => if *silent { + return Box::new(empty()); + } else { + return Box::new(once(Err(format_err!( + "The handler supplied was unable to evaluate the given service" + )))) as EncodedTuplesIterator<'_>; + }, + Some(term) => { + match self.dataset.decode_named_node(term) { + Err(err) => if *silent { + return Box::new(empty()); + } else { + return Box::new(once(Err(err))) as EncodedTuplesIterator<'_>; + }, + Ok(named_node) => { + println!("named_node: {:?}", named_node); + handler.handle(named_node) + } + + } + }, + }; + match pattern_option { + None => if *silent { + return Box::new(empty()); + } else { + return Box::new(once(Err(format_err!( + "The handler supplied was unable to produce any result set on the given service" + )))) as EncodedTuplesIterator<'_>; + }, + Some(pattern_fn) => { + match pattern_fn(graph_pattern.clone()) { + Ok(bindings) => { + let encoded = self.encode_bindings(variables, bindings); + let collected = encoded.collect::>(); + Box::new(JoinIterator { + left: vec![from], + right_iter: Box::new(collected.into_iter()), + buffered_results: vec![], + }) + }, + Err(err) => { + if *silent { + return Box::new(empty()); + } else { + return Box::new(once(Err(err))) as EncodedTuplesIterator<'_> + } + } + } + }, + } + } + } + }, PlanNode::QuadPatternJoin { child, subject, predicate, object, graph_name, - } => Box::new(self.eval_plan(&*child, from).flat_map_ok(move |tuple| { + } => Box::new(self.eval_plan(&*child, from, options).flat_map_ok(move |tuple| { let mut iter = self.dataset.quads_for_pattern( get_pattern_value(&subject, &tuple), 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 { @@ -186,7 +275,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { path, object, graph_name, - } => Box::new(self.eval_plan(&*child, from).flat_map_ok(move |tuple| { + } => Box::new(self.eval_plan(&*child, from, options).flat_map_ok(move |tuple| { let input_subject = get_pattern_value(&subject, &tuple); let input_object = get_pattern_value(&object, &tuple); let input_graph_name = @@ -199,7 +288,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 { @@ -213,7 +302,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); @@ -221,7 +310,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); @@ -229,7 +318,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); @@ -244,7 +333,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { //TODO: very dumb implementation let mut errors = Vec::default(); let left_values = self - .eval_plan(&*left, from.clone()) + .eval_plan(&*left, from.clone(), options) .filter_map(|result| match result { Ok(result) => Some(result), Err(error) => { @@ -255,18 +344,18 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { .collect::>(); Box::new(JoinIterator { left: left_values, - right_iter: self.eval_plan(&*right, from), + right_iter: self.eval_plan(&*right, from, options), buffered_results: errors, }) } PlanNode::AntiJoin { left, right } => { //TODO: dumb implementation let right: Vec<_> = self - .eval_plan(&*right, from.clone()) + .eval_plan(&*right, from.clone(), options) .filter_map(|result| result.ok()) .collect(); Box::new(AntiJoinIterator { - left_iter: self.eval_plan(&*left, from), + left_iter: self.eval_plan(&*left, from, options), right, }) } @@ -281,8 +370,9 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { let iter = LeftJoinIterator { eval: self, right_plan: &*right, - left_iter: self.eval_plan(&*left, filtered_from), + left_iter: self.eval_plan(&*left, filtered_from, options), current_right: Box::new(empty()), + options, }; if problem_vars.is_empty() { Box::new(iter) @@ -296,10 +386,10 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } PlanNode::Filter { child, expression } => { let eval = self; - Box::new(self.eval_plan(&*child, from).filter(move |tuple| { + Box::new(self.eval_plan(&*child, from, options).filter(move |tuple| { match tuple { Ok(tuple) => eval - .eval_expression(&expression, tuple) + .eval_expression(&expression, tuple, options) .and_then(|term| eval.to_bool(term)) .unwrap_or(false), Err(_) => true, @@ -312,6 +402,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { input: from, current_iterator: Box::new(empty()), current_plan: 0, + options, }), PlanNode::Extend { child, @@ -319,9 +410,9 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { expression, } => { let eval = self; - Box::new(self.eval_plan(&*child, from).map(move |tuple| { + Box::new(self.eval_plan(&*child, from, options).map(move |tuple| { let mut tuple = tuple?; - if let Some(value) = eval.eval_expression(&expression, &tuple) { + if let Some(value) = eval.eval_expression(&expression, &tuple, options) { put_value(*position, value, &mut tuple) } Ok(tuple) @@ -330,7 +421,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { PlanNode::Sort { child, by } => { let mut errors = Vec::default(); let mut values = self - .eval_plan(&*child, from) + .eval_plan(&*child, from, options) .filter_map(|result| match result { Ok(result) => Some(result), Err(error) => { @@ -343,14 +434,14 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { for comp in by { match comp { Comparator::Asc(expression) => { - match self.cmp_according_to_expression(a, b, &expression) { + match self.cmp_according_to_expression(a, b, &expression, options) { Ordering::Greater => return Ordering::Greater, Ordering::Less => return Ordering::Less, Ordering::Equal => (), } } Comparator::Desc(expression) => { - match self.cmp_according_to_expression(a, b, &expression) { + match self.cmp_according_to_expression(a, b, &expression, options) { Ordering::Greater => return Ordering::Less, Ordering::Less => return Ordering::Greater, Ordering::Equal => (), @@ -363,16 +454,16 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { Box::new(errors.into_iter().chain(values.into_iter().map(Ok))) } PlanNode::HashDeduplicate { child } => { - Box::new(hash_deduplicate(self.eval_plan(&*child, from))) + Box::new(hash_deduplicate(self.eval_plan(&*child, from, options))) } - PlanNode::Skip { child, count } => Box::new(self.eval_plan(&*child, from).skip(*count)), + PlanNode::Skip { child, count } => Box::new(self.eval_plan(&*child, from, options).skip(*count)), PlanNode::Limit { child, count } => { - Box::new(self.eval_plan(&*child, from).take(*count)) + Box::new(self.eval_plan(&*child, from, options).take(*count)) } PlanNode::Project { child, mapping } => { //TODO: use from somewhere? Box::new( - self.eval_plan(&*child, vec![None; mapping.len()]) + self.eval_plan(&*child, vec![None; mapping.len()], options) .map(move |tuple| { let tuple = tuple?; let mut output_tuple = vec![None; from.len()]; @@ -394,7 +485,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { let mut errors = Vec::default(); let mut accumulators_for_group = HashMap::>, Vec>>::default(); - self.eval_plan(child, from) + self.eval_plan(child, from, options) .filter_map(|result| match result { Ok(result) => Some(result), Err(error) => { @@ -426,7 +517,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { aggregate .parameter .as_ref() - .and_then(|parameter| self.eval_expression(¶meter, &tuple)), + .and_then(|parameter| self.eval_expression(¶meter, &tuple, options)), ); } }); @@ -506,6 +597,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { path: &'b PlanPropertyPath, start: EncodedTerm, graph_name: EncodedTerm, + options: &'b QueryOptions<'b> ) -> Box> + 'b> where 'a: 'b, @@ -513,33 +605,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, @@ -554,6 +646,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { path: &'b PlanPropertyPath, end: EncodedTerm, graph_name: EncodedTerm, + options: &'a QueryOptions<'b> ) -> Box> + 'b> where 'a: 'b, @@ -561,33 +654,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, @@ -601,6 +694,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { &'b self, path: &'b PlanPropertyPath, graph_name: EncodedTerm, + options: &'b QueryOptions<'b> ) -> Box> + 'b> where 'a: 'b, @@ -608,45 +702,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, @@ -659,31 +753,33 @@ 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))) } - fn eval_expression( - &self, + fn eval_expression<'b>( + &'b self, expression: &PlanExpression, tuple: &[Option], + options: &QueryOptions<'b> ) -> Option { match expression { PlanExpression::Constant(t) => Some(*t), PlanExpression::Variable(v) => get_tuple_value(*v, tuple), PlanExpression::Exists(node) => { - Some(self.eval_plan(node, tuple.to_vec()).next().is_some().into()) + Some(self.eval_plan(node, tuple.to_vec(), options).next().is_some().into()) } PlanExpression::Or(a, b) => { - match self.eval_expression(a, tuple).and_then(|v| self.to_bool(v)) { + match self.eval_expression(a, tuple, options).and_then(|v| self.to_bool(v)) { Some(true) => Some(true.into()), - Some(false) => self.eval_expression(b, tuple), + Some(false) => self.eval_expression(b, tuple, options), None => { if Some(true) - == self.eval_expression(b, tuple).and_then(|v| self.to_bool(v)) + == self.eval_expression(b, tuple, options).and_then(|v| self.to_bool(v)) { Some(true.into()) } else { @@ -693,13 +789,13 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } } PlanExpression::And(a, b) => match self - .eval_expression(a, tuple) + .eval_expression(a, tuple, options) .and_then(|v| self.to_bool(v)) { - Some(true) => self.eval_expression(b, tuple), + Some(true) => self.eval_expression(b, tuple, options), Some(false) => Some(false.into()), None => { - if Some(false) == self.eval_expression(b, tuple).and_then(|v| self.to_bool(v)) { + if Some(false) == self.eval_expression(b, tuple, options).and_then(|v| self.to_bool(v)) { Some(false.into()) } else { None @@ -707,26 +803,26 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } }, PlanExpression::Equal(a, b) => { - let a = self.eval_expression(a, tuple)?; - let b = self.eval_expression(b, tuple)?; + let a = self.eval_expression(a, tuple, options)?; + let b = self.eval_expression(b, tuple, options)?; self.equals(a, b).map(|v| v.into()) } PlanExpression::NotEqual(a, b) => { - let a = self.eval_expression(a, tuple)?; - let b = self.eval_expression(b, tuple)?; + let a = self.eval_expression(a, tuple, options)?; + let b = self.eval_expression(b, tuple, options)?; self.equals(a, b).map(|v| (!v).into()) } PlanExpression::Greater(a, b) => Some( (self.partial_cmp_literals( - self.eval_expression(a, tuple)?, - self.eval_expression(b, tuple)?, + self.eval_expression(a, tuple, options)?, + self.eval_expression(b, tuple, options)?, )? == Ordering::Greater) .into(), ), PlanExpression::GreaterOrEq(a, b) => Some( match self.partial_cmp_literals( - self.eval_expression(a, tuple)?, - self.eval_expression(b, tuple)?, + self.eval_expression(a, tuple, options)?, + self.eval_expression(b, tuple, options)?, )? { Ordering::Greater | Ordering::Equal => true, Ordering::Less => false, @@ -735,15 +831,15 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { ), PlanExpression::Lower(a, b) => Some( (self.partial_cmp_literals( - self.eval_expression(a, tuple)?, - self.eval_expression(b, tuple)?, + self.eval_expression(a, tuple, options)?, + self.eval_expression(b, tuple, options)?, )? == Ordering::Less) .into(), ), PlanExpression::LowerOrEq(a, b) => Some( match self.partial_cmp_literals( - self.eval_expression(a, tuple)?, - self.eval_expression(b, tuple)?, + self.eval_expression(a, tuple, options)?, + self.eval_expression(b, tuple, options)?, )? { Ordering::Less | Ordering::Equal => true, Ordering::Greater => false, @@ -751,10 +847,10 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { .into(), ), PlanExpression::In(e, l) => { - let needed = self.eval_expression(e, tuple)?; + let needed = self.eval_expression(e, tuple, options)?; let mut error = false; for possible in l { - if let Some(possible) = self.eval_expression(possible, tuple) { + if let Some(possible) = self.eval_expression(possible, tuple, options) { if Some(true) == self.equals(needed, possible) { return Some(true.into()); } @@ -768,25 +864,25 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { Some(false.into()) } } - PlanExpression::Add(a, b) => Some(match self.parse_numeric_operands(a, b, tuple)? { + PlanExpression::Add(a, b) => Some(match self.parse_numeric_operands(a, b, tuple, options)? { NumericBinaryOperands::Float(v1, v2) => (v1 + v2).into(), NumericBinaryOperands::Double(v1, v2) => (v1 + v2).into(), NumericBinaryOperands::Integer(v1, v2) => v1.checked_add(v2)?.into(), NumericBinaryOperands::Decimal(v1, v2) => v1.checked_add(v2)?.into(), }), - PlanExpression::Sub(a, b) => Some(match self.parse_numeric_operands(a, b, tuple)? { + PlanExpression::Sub(a, b) => Some(match self.parse_numeric_operands(a, b, tuple, options)? { NumericBinaryOperands::Float(v1, v2) => (v1 - v2).into(), NumericBinaryOperands::Double(v1, v2) => (v1 - v2).into(), NumericBinaryOperands::Integer(v1, v2) => v1.checked_sub(v2)?.into(), NumericBinaryOperands::Decimal(v1, v2) => v1.checked_sub(v2)?.into(), }), - PlanExpression::Mul(a, b) => Some(match self.parse_numeric_operands(a, b, tuple)? { + PlanExpression::Mul(a, b) => Some(match self.parse_numeric_operands(a, b, tuple, options)? { NumericBinaryOperands::Float(v1, v2) => (v1 * v2).into(), NumericBinaryOperands::Double(v1, v2) => (v1 * v2).into(), NumericBinaryOperands::Integer(v1, v2) => v1.checked_mul(v2)?.into(), NumericBinaryOperands::Decimal(v1, v2) => v1.checked_mul(v2)?.into(), }), - PlanExpression::Div(a, b) => Some(match self.parse_numeric_operands(a, b, tuple)? { + PlanExpression::Div(a, b) => Some(match self.parse_numeric_operands(a, b, tuple, options)? { NumericBinaryOperands::Float(v1, v2) => (v1 / v2).into(), NumericBinaryOperands::Double(v1, v2) => (v1 / v2).into(), NumericBinaryOperands::Integer(v1, v2) => Decimal::from_i128(v1)? @@ -794,14 +890,14 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { .into(), NumericBinaryOperands::Decimal(v1, v2) => v1.checked_div(v2)?.into(), }), - PlanExpression::UnaryPlus(e) => match self.eval_expression(e, tuple)? { + PlanExpression::UnaryPlus(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::FloatLiteral(value) => Some((*value).into()), EncodedTerm::DoubleLiteral(value) => Some((*value).into()), EncodedTerm::IntegerLiteral(value) => Some((value).into()), EncodedTerm::DecimalLiteral(value) => Some((value).into()), _ => None, }, - PlanExpression::UnaryMinus(e) => match self.eval_expression(e, tuple)? { + PlanExpression::UnaryMinus(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::FloatLiteral(value) => Some((-*value).into()), EncodedTerm::DoubleLiteral(value) => Some((-*value).into()), EncodedTerm::IntegerLiteral(value) => Some((-value).into()), @@ -809,12 +905,12 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { _ => None, }, PlanExpression::UnaryNot(e) => self - .to_bool(self.eval_expression(e, tuple)?) + .to_bool(self.eval_expression(e, tuple, options)?) .map(|v| (!v).into()), PlanExpression::Str(e) => Some(EncodedTerm::StringLiteral { - value_id: self.to_string_id(self.eval_expression(e, tuple)?)?, + value_id: self.to_string_id(self.eval_expression(e, tuple, options)?)?, }), - PlanExpression::Lang(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Lang(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::LangStringLiteral { language_id, .. } => { Some(EncodedTerm::StringLiteral { value_id: language_id, @@ -825,9 +921,9 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { }, PlanExpression::LangMatches(language_tag, language_range) => { let language_tag = - self.to_simple_string(self.eval_expression(language_tag, tuple)?)?; + self.to_simple_string(self.eval_expression(language_tag, tuple, options)?)?; let language_range = - self.to_simple_string(self.eval_expression(language_range, tuple)?)?; + self.to_simple_string(self.eval_expression(language_range, tuple, options)?)?; Some( if &*language_range == "*" { !language_tag.is_empty() @@ -845,16 +941,19 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { .into(), ) } - PlanExpression::Datatype(e) => self.eval_expression(e, tuple)?.datatype(), + PlanExpression::Datatype(e) => self.eval_expression(e, tuple, options)?.datatype(), PlanExpression::Bound(v) => Some(has_tuple_value(*v, tuple).into()), PlanExpression::IRI(e) => { - let iri_id = match self.eval_expression(e, tuple)? { + let iri_id = match self.eval_expression(e, tuple, options)? { EncodedTerm::NamedNode { iri_id } => Some(iri_id), EncodedTerm::StringLiteral { value_id } => Some(value_id), _ => 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.to_string())) + .or(self.base_iri.as_ref().map(|iri| Ok(iri.clone()))); + if let Some(Ok(base_iri)) = base_iri { self.build_named_node(&base_iri.resolve(&iri).ok()?.into_inner()) } else { Iri::parse(iri).ok()?; @@ -864,7 +963,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { PlanExpression::BNode(id) => match id { Some(id) => { if let EncodedTerm::StringLiteral { value_id } = - self.eval_expression(id, tuple)? + self.eval_expression(id, tuple, options)? { Some(EncodedTerm::BlankNode { id: *self @@ -883,28 +982,28 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { }), }, PlanExpression::Rand => Some(random::().into()), - PlanExpression::Abs(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Abs(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::IntegerLiteral(value) => Some(value.checked_abs()?.into()), EncodedTerm::DecimalLiteral(value) => Some(value.abs().into()), EncodedTerm::FloatLiteral(value) => Some(value.abs().into()), EncodedTerm::DoubleLiteral(value) => Some(value.abs().into()), _ => None, }, - PlanExpression::Ceil(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Ceil(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::IntegerLiteral(value) => Some(value.into()), EncodedTerm::DecimalLiteral(value) => Some(value.ceil().into()), EncodedTerm::FloatLiteral(value) => Some(value.ceil().into()), EncodedTerm::DoubleLiteral(value) => Some(value.ceil().into()), _ => None, }, - PlanExpression::Floor(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Floor(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::IntegerLiteral(value) => Some(value.into()), EncodedTerm::DecimalLiteral(value) => Some(value.floor().into()), EncodedTerm::FloatLiteral(value) => Some(value.floor().into()), EncodedTerm::DoubleLiteral(value) => Some(value.floor().into()), _ => None, }, - PlanExpression::Round(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Round(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::IntegerLiteral(value) => Some(value.into()), EncodedTerm::DecimalLiteral(value) => Some( value @@ -920,7 +1019,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { let mut language = None; for e in l { let (value, e_language) = - self.to_string_and_language(self.eval_expression(e, tuple)?)?; + self.to_string_and_language(self.eval_expression(e, tuple, options)?)?; if let Some(lang) = language { if lang != e_language { language = Some(None) @@ -934,17 +1033,17 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } PlanExpression::SubStr(source, starting_loc, length) => { let (source, language) = - self.to_string_and_language(self.eval_expression(source, tuple)?)?; + self.to_string_and_language(self.eval_expression(source, tuple, options)?)?; let starting_location: usize = if let EncodedTerm::IntegerLiteral(v) = - self.eval_expression(starting_loc, tuple)? + self.eval_expression(starting_loc, tuple, options)? { v.try_into().ok()? } else { return None; }; let length: Option = if let Some(length) = length { - if let EncodedTerm::IntegerLiteral(v) = self.eval_expression(length, tuple)? { + if let EncodedTerm::IntegerLiteral(v) = self.eval_expression(length, tuple, options)? { Some(v.try_into().ok()?) } else { return None; @@ -976,45 +1075,45 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } PlanExpression::StrLen(arg) => Some( (self - .to_string(self.eval_expression(arg, tuple)?)? + .to_string(self.eval_expression(arg, tuple, options)?)? .chars() .count() as i128) .into(), ), PlanExpression::Replace(arg, pattern, replacement, flags) => { let regex = self.compile_pattern( - self.eval_expression(pattern, tuple)?, + self.eval_expression(pattern, tuple, options)?, if let Some(flags) = flags { - Some(self.eval_expression(flags, tuple)?) + Some(self.eval_expression(flags, tuple, options)?) } else { None }, )?; let (text, language) = - self.to_string_and_language(self.eval_expression(arg, tuple)?)?; + self.to_string_and_language(self.eval_expression(arg, tuple, options)?)?; let replacement = - self.to_simple_string(self.eval_expression(replacement, tuple)?)?; + self.to_simple_string(self.eval_expression(replacement, tuple, options)?)?; self.build_plain_literal(®ex.replace_all(&text, &replacement as &str), language) } PlanExpression::UCase(e) => { let (value, language) = - self.to_string_and_language(self.eval_expression(e, tuple)?)?; + self.to_string_and_language(self.eval_expression(e, tuple, options)?)?; self.build_plain_literal(&value.to_uppercase(), language) } PlanExpression::LCase(e) => { let (value, language) = - self.to_string_and_language(self.eval_expression(e, tuple)?)?; + self.to_string_and_language(self.eval_expression(e, tuple, options)?)?; self.build_plain_literal(&value.to_lowercase(), language) } PlanExpression::StrStarts(arg1, arg2) => { let (arg1, arg2, _) = self.to_argument_compatible_strings( - self.eval_expression(arg1, tuple)?, - self.eval_expression(arg2, tuple)?, + self.eval_expression(arg1, tuple, options)?, + self.eval_expression(arg2, tuple, options)?, )?; Some((&arg1).starts_with(&arg2 as &str).into()) } PlanExpression::EncodeForURI(ltrl) => { - let ltlr = self.to_string(self.eval_expression(ltrl, tuple)?)?; + let ltlr = self.to_string(self.eval_expression(ltrl, tuple, options)?)?; let mut result = Vec::with_capacity(ltlr.len()); for c in ltlr.bytes() { match c { @@ -1042,22 +1141,22 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } PlanExpression::StrEnds(arg1, arg2) => { let (arg1, arg2, _) = self.to_argument_compatible_strings( - self.eval_expression(arg1, tuple)?, - self.eval_expression(arg2, tuple)?, + self.eval_expression(arg1, tuple, options)?, + self.eval_expression(arg2, tuple, options)?, )?; Some((&arg1).ends_with(&arg2 as &str).into()) } PlanExpression::Contains(arg1, arg2) => { let (arg1, arg2, _) = self.to_argument_compatible_strings( - self.eval_expression(arg1, tuple)?, - self.eval_expression(arg2, tuple)?, + self.eval_expression(arg1, tuple, options)?, + self.eval_expression(arg2, tuple, options)?, )?; Some((&arg1).contains(&arg2 as &str).into()) } PlanExpression::StrBefore(arg1, arg2) => { let (arg1, arg2, language) = self.to_argument_compatible_strings( - self.eval_expression(arg1, tuple)?, - self.eval_expression(arg2, tuple)?, + self.eval_expression(arg1, tuple, options)?, + self.eval_expression(arg2, tuple, options)?, )?; if let Some(position) = (&arg1).find(&arg2 as &str) { self.build_plain_literal(&arg1[..position], language) @@ -1067,8 +1166,8 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } PlanExpression::StrAfter(arg1, arg2) => { let (arg1, arg2, language) = self.to_argument_compatible_strings( - self.eval_expression(arg1, tuple)?, - self.eval_expression(arg2, tuple)?, + self.eval_expression(arg1, tuple, options)?, + self.eval_expression(arg2, tuple, options)?, )?; if let Some(position) = (&arg1).find(&arg2 as &str) { self.build_plain_literal(&arg1[position + arg2.len()..], language) @@ -1076,40 +1175,40 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { Some(ENCODED_EMPTY_STRING_LITERAL) } } - PlanExpression::Year(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Year(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::DateLiteral(date) => Some(date.year().into()), EncodedTerm::NaiveDateLiteral(date) => Some(date.year().into()), EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.year().into()), EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.year().into()), _ => None, }, - PlanExpression::Month(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Month(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::DateLiteral(date) => Some(date.year().into()), EncodedTerm::NaiveDateLiteral(date) => Some(date.month().into()), EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.month().into()), EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.month().into()), _ => None, }, - PlanExpression::Day(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Day(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::DateLiteral(date) => Some(date.year().into()), EncodedTerm::NaiveDateLiteral(date) => Some(date.day().into()), EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.day().into()), EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.day().into()), _ => None, }, - PlanExpression::Hours(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Hours(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::NaiveTimeLiteral(time) => Some(time.hour().into()), EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.hour().into()), EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.hour().into()), _ => None, }, - PlanExpression::Minutes(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Minutes(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::NaiveTimeLiteral(time) => Some(time.minute().into()), EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.minute().into()), EncodedTerm::NaiveDateTimeLiteral(date_time) => Some(date_time.minute().into()), _ => None, }, - PlanExpression::Seconds(e) => match self.eval_expression(e, tuple)? { + PlanExpression::Seconds(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::NaiveTimeLiteral(time) => Some( (Decimal::new(time.nanosecond().into(), 9) + Decimal::from(time.second())) .into(), @@ -1127,7 +1226,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { _ => None, }, PlanExpression::Timezone(e) => { - let timezone = match self.eval_expression(e, tuple)? { + let timezone = match self.eval_expression(e, tuple, options)? { EncodedTerm::DateLiteral(date) => date.timezone(), EncodedTerm::DateTimeLiteral(date_time) => date_time.timezone(), _ => return None, @@ -1161,7 +1260,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { }) } PlanExpression::Tz(e) => { - let timezone = match self.eval_expression(e, tuple)? { + let timezone = match self.eval_expression(e, tuple, options)? { EncodedTerm::DateLiteral(date) => Some(date.timezone()), EncodedTerm::DateTimeLiteral(date_time) => Some(date_time.timezone()), EncodedTerm::NaiveDateLiteral(_) @@ -1192,38 +1291,38 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { .to_hyphenated() .encode_lower(&mut Uuid::encode_buffer()), ), - PlanExpression::MD5(arg) => self.hash::(arg, tuple), - PlanExpression::SHA1(arg) => self.hash::(arg, tuple), - PlanExpression::SHA256(arg) => self.hash::(arg, tuple), - PlanExpression::SHA384(arg) => self.hash::(arg, tuple), - PlanExpression::SHA512(arg) => self.hash::(arg, tuple), + PlanExpression::MD5(arg) => self.hash::(arg, tuple, options), + PlanExpression::SHA1(arg) => self.hash::(arg, tuple, options), + PlanExpression::SHA256(arg) => self.hash::(arg, tuple, options), + PlanExpression::SHA384(arg) => self.hash::(arg, tuple, options), + PlanExpression::SHA512(arg) => self.hash::(arg, tuple, options), PlanExpression::Coalesce(l) => { for e in l { - if let Some(result) = self.eval_expression(e, tuple) { + if let Some(result) = self.eval_expression(e, tuple, options) { return Some(result); } } None } PlanExpression::If(a, b, c) => { - if self.to_bool(self.eval_expression(a, tuple)?)? { - self.eval_expression(b, tuple) + if self.to_bool(self.eval_expression(a, tuple, options)?)? { + self.eval_expression(b, tuple, options) } else { - self.eval_expression(c, tuple) + self.eval_expression(c, tuple, options) } } PlanExpression::StrLang(lexical_form, lang_tag) => { Some(EncodedTerm::LangStringLiteral { value_id: self - .to_simple_string_id(self.eval_expression(lexical_form, tuple)?)?, + .to_simple_string_id(self.eval_expression(lexical_form, tuple, options)?)?, language_id: self - .to_simple_string_id(self.eval_expression(lang_tag, tuple)?)?, + .to_simple_string_id(self.eval_expression(lang_tag, tuple, options)?)?, }) } PlanExpression::StrDT(lexical_form, datatype) => { - let value = self.to_simple_string(self.eval_expression(lexical_form, tuple)?)?; + let value = self.to_simple_string(self.eval_expression(lexical_form, tuple, options)?)?; let datatype = if let EncodedTerm::NamedNode { iri_id } = - self.eval_expression(datatype, tuple)? + self.eval_expression(datatype, tuple, options)? { self.dataset.get_str(iri_id).ok()? } else { @@ -1238,19 +1337,19 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { .ok() } PlanExpression::SameTerm(a, b) => { - Some((self.eval_expression(a, tuple)? == self.eval_expression(b, tuple)?).into()) + Some((self.eval_expression(a, tuple, options)? == self.eval_expression(b, tuple, options)?).into()) } PlanExpression::IsIRI(e) => { - Some(self.eval_expression(e, tuple)?.is_named_node().into()) + Some(self.eval_expression(e, tuple, options)?.is_named_node().into()) } PlanExpression::IsBlank(e) => { - Some(self.eval_expression(e, tuple)?.is_blank_node().into()) + Some(self.eval_expression(e, tuple, options)?.is_blank_node().into()) } PlanExpression::IsLiteral(e) => { - Some(self.eval_expression(e, tuple)?.is_literal().into()) + Some(self.eval_expression(e, tuple, options)?.is_literal().into()) } PlanExpression::IsNumeric(e) => Some( - match self.eval_expression(e, tuple)? { + match self.eval_expression(e, tuple, options)? { EncodedTerm::FloatLiteral(_) | EncodedTerm::DoubleLiteral(_) | EncodedTerm::IntegerLiteral(_) @@ -1261,24 +1360,24 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { ), PlanExpression::Regex(text, pattern, flags) => { let regex = self.compile_pattern( - self.eval_expression(pattern, tuple)?, + self.eval_expression(pattern, tuple, options)?, if let Some(flags) = flags { - Some(self.eval_expression(flags, tuple)?) + Some(self.eval_expression(flags, tuple, options)?) } else { None }, )?; - let text = self.to_string(self.eval_expression(text, tuple)?)?; + let text = self.to_string(self.eval_expression(text, tuple, options)?)?; Some(regex.is_match(&text).into()) } - PlanExpression::BooleanCast(e) => match self.eval_expression(e, tuple)? { + PlanExpression::BooleanCast(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::BooleanLiteral(value) => Some(value.into()), EncodedTerm::StringLiteral { value_id } => { parse_boolean_str(&*self.dataset.get_str(value_id).ok()??) } _ => None, }, - PlanExpression::DoubleCast(e) => match self.eval_expression(e, tuple)? { + PlanExpression::DoubleCast(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::FloatLiteral(value) => Some(value.to_f64()?.into()), EncodedTerm::DoubleLiteral(value) => Some(value.to_f64()?.into()), EncodedTerm::IntegerLiteral(value) => Some(value.to_f64()?.into()), @@ -1291,7 +1390,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } _ => None, }, - PlanExpression::FloatCast(e) => match self.eval_expression(e, tuple)? { + PlanExpression::FloatCast(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::FloatLiteral(value) => Some(value.to_f32()?.into()), EncodedTerm::DoubleLiteral(value) => Some(value.to_f32()?.into()), EncodedTerm::IntegerLiteral(value) => Some(value.to_f32()?.into()), @@ -1304,7 +1403,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } _ => None, }, - PlanExpression::IntegerCast(e) => match self.eval_expression(e, tuple)? { + PlanExpression::IntegerCast(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::FloatLiteral(value) => Some(value.to_i128()?.into()), EncodedTerm::DoubleLiteral(value) => Some(value.to_i128()?.into()), EncodedTerm::IntegerLiteral(value) => Some(value.to_i128()?.into()), @@ -1315,7 +1414,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } _ => None, }, - PlanExpression::DecimalCast(e) => match self.eval_expression(e, tuple)? { + PlanExpression::DecimalCast(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::FloatLiteral(value) => Some(Decimal::from_f32(*value)?.into()), EncodedTerm::DoubleLiteral(value) => Some(Decimal::from_f64(*value)?.into()), EncodedTerm::IntegerLiteral(value) => Some(Decimal::from_i128(value)?.into()), @@ -1333,7 +1432,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } _ => None, }, - PlanExpression::DateCast(e) => match self.eval_expression(e, tuple)? { + PlanExpression::DateCast(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::DateLiteral(value) => Some(value.into()), EncodedTerm::NaiveDateLiteral(value) => Some(value.into()), EncodedTerm::DateTimeLiteral(value) => Some(value.date().into()), @@ -1343,7 +1442,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } _ => None, }, - PlanExpression::TimeCast(e) => match self.eval_expression(e, tuple)? { + PlanExpression::TimeCast(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::NaiveTimeLiteral(value) => Some(value.into()), EncodedTerm::DateTimeLiteral(value) => Some(value.time().into()), EncodedTerm::NaiveDateTimeLiteral(value) => Some(value.time().into()), @@ -1352,7 +1451,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } _ => None, }, - PlanExpression::DateTimeCast(e) => match self.eval_expression(e, tuple)? { + PlanExpression::DateTimeCast(e) => match self.eval_expression(e, tuple, options)? { EncodedTerm::DateTimeLiteral(value) => Some(value.into()), EncodedTerm::NaiveDateTimeLiteral(value) => Some(value.into()), EncodedTerm::StringLiteral { value_id } => { @@ -1361,7 +1460,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { _ => None, }, PlanExpression::StringCast(e) => Some(EncodedTerm::StringLiteral { - value_id: self.to_string_id(self.eval_expression(e, tuple)?)?, + value_id: self.to_string_id(self.eval_expression(e, tuple, options)?)?, }), } } @@ -1526,15 +1625,16 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { regex_builder.build().ok() } - fn parse_numeric_operands( - &self, + fn parse_numeric_operands<'b>( + &'b self, e1: &PlanExpression, e2: &PlanExpression, tuple: &[Option], + options: &QueryOptions<'b> ) -> Option { NumericBinaryOperands::new( - self.eval_expression(&e1, tuple)?, - self.eval_expression(&e2, tuple)?, + self.eval_expression(&e1, tuple, options)?, + self.eval_expression(&e2, tuple, options)?, ) } @@ -1562,6 +1662,41 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { ) } + // this is used to encode results froma BindingIterator into an EncodedTuplesIterator. This happens when SERVICE clauses are evaluated + fn encode_bindings<'b>( + &'b self, + variables: &'b [Variable], + iter: BindingsIterator<'b>, + ) -> EncodedTuplesIterator<'b> + where + 'a: 'b, + { + let mut encoder = self.dataset.encoder(); + let (binding_variables, iter) = BindingsIterator::destruct(iter); + let mut combined_variables = variables.to_vec(); + for v in binding_variables.clone() { + if !combined_variables.contains(&v) { + combined_variables.resize(combined_variables.len() + 1, v); + } + } + Box::new(iter.map(move |terms| { + let mut encoded_terms = vec![None; combined_variables.len()]; + for (i, term_option) in terms?.into_iter().enumerate() { + match term_option { + None => (), + Some(term) => { + if let Ok(encoded) = encoder.encode_term(&term) { + let variable = binding_variables[i].clone(); + put_variable_value(&variable, &combined_variables, encoded, &mut encoded_terms) + } + } + } + } + Ok(encoded_terms) + })) + } + + #[allow(clippy::float_cmp)] fn equals(&self, a: EncodedTerm, b: EncodedTerm) -> Option { match a { @@ -1674,15 +1809,16 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } } - fn cmp_according_to_expression( - &self, + fn cmp_according_to_expression<'b>( + &'b self, tuple_a: &[Option], tuple_b: &[Option], expression: &PlanExpression, + options: &QueryOptions<'b> ) -> Ordering { self.cmp_terms( - self.eval_expression(expression, tuple_a), - self.eval_expression(expression, tuple_b), + self.eval_expression(expression, tuple_a, options), + self.eval_expression(expression, tuple_b, options), ) } @@ -1793,12 +1929,13 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { ) } - fn hash( - &self, + fn hash<'b, H: Digest>( + &'b self, arg: &PlanExpression, tuple: &[Option], + options: &QueryOptions<'b> ) -> Option { - let input = self.to_simple_string(self.eval_expression(arg, tuple)?)?; + let input = self.to_simple_string(self.eval_expression(arg, tuple, options)?)?; let hash = hex::encode(H::new().chain(&input as &str).result()); self.build_string_literal(&hash) } @@ -1934,6 +2071,16 @@ fn put_pattern_value(selector: &PatternValue, value: EncodedTerm, tuple: &mut En } } +fn put_variable_value(selector: &Variable, variables: &[Variable], value: EncodedTerm, tuple: &mut EncodedTuple) { + for (i, v) in variables.iter().enumerate() { + if selector == v { + put_value(i, value, tuple); + break; + } + } +} + + fn put_value(position: usize, value: EncodedTerm, tuple: &mut EncodedTuple) { if position < tuple.len() { tuple[position] = Some(value) @@ -2067,6 +2214,7 @@ struct LeftJoinIterator<'a, S: StoreConnection + 'a> { right_plan: &'a PlanNode, left_iter: EncodedTuplesIterator<'a>, current_right: EncodedTuplesIterator<'a>, + options: &'a QueryOptions<'a>, } impl<'a, S: StoreConnection> Iterator for LeftJoinIterator<'a, S> { @@ -2078,7 +2226,7 @@ impl<'a, S: StoreConnection> Iterator for LeftJoinIterator<'a, S> { } match self.left_iter.next()? { Ok(left_tuple) => { - self.current_right = self.eval.eval_plan(self.right_plan, left_tuple.clone()); + self.current_right = self.eval.eval_plan(self.right_plan, left_tuple.clone(), self.options); if let Some(right_tuple) = self.current_right.next() { Some(right_tuple) } else { @@ -2132,6 +2280,7 @@ struct UnionIterator<'a, S: StoreConnection + 'a> { input: EncodedTuple, current_iterator: EncodedTuplesIterator<'a>, current_plan: usize, + options: &'a QueryOptions<'a>, } impl<'a, S: StoreConnection> Iterator for UnionIterator<'a, S> { @@ -2147,13 +2296,13 @@ impl<'a, S: StoreConnection> Iterator for UnionIterator<'a, S> { } self.current_iterator = self .eval - .eval_plan(&self.plans[self.current_plan], self.input.clone()); + .eval_plan(&self.plans[self.current_plan], self.input.clone(), self.options); self.current_plan += 1; } } } -struct ConstructIterator<'a, S: StoreConnection> { +struct ConstructIterator<'a, S: StoreConnection + 'a> { eval: &'a SimpleEvaluator, iter: EncodedTuplesIterator<'a>, template: &'a [TripleTemplate], @@ -2161,7 +2310,7 @@ struct ConstructIterator<'a, S: StoreConnection> { bnodes: Vec, } -impl<'a, S: StoreConnection> Iterator for ConstructIterator<'a, S> { +impl<'a, S: StoreConnection + 'a> Iterator for ConstructIterator<'a, S> { type Item = Result; fn next(&mut self) -> Option> { @@ -2226,11 +2375,12 @@ fn decode_triple( struct DescribeIterator<'a, S: StoreConnection + 'a> { eval: &'a SimpleEvaluator, + options: &'a QueryOptions<'a>, iter: EncodedTuplesIterator<'a>, quads: Box> + 'a>, } -impl<'a, S: StoreConnection> Iterator for DescribeIterator<'a, S> { +impl<'a, S: StoreConnection + 'a> Iterator for DescribeIterator<'a, S> { type Item = Result; fn next(&mut self) -> Option> { @@ -2254,7 +2404,7 @@ impl<'a, S: StoreConnection> 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 dbbbdb6c..5593b3b7 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -9,6 +9,7 @@ mod plan; mod plan_builder; mod xml_results; +use crate::model::NamedNode; use crate::sparql::algebra::QueryVariants; use crate::sparql::eval::SimpleEvaluator; use crate::sparql::parser::read_sparql_query; @@ -17,8 +18,11 @@ use crate::sparql::plan::{DatasetView, PlanNode}; use crate::sparql::plan_builder::PlanBuilder; use crate::store::StoreConnection; use crate::Result; +use failure::format_err; +use rio_api::iri::Iri; use std::fmt; +pub use crate::sparql::algebra::GraphPattern; pub use crate::sparql::model::BindingsIterator; pub use crate::sparql::model::QueryResult; pub use crate::sparql::model::QueryResultSyntax; @@ -27,7 +31,7 @@ pub use crate::sparql::model::Variable; /// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/) pub trait PreparedQuery { /// Evaluates the query and returns its results - fn exec(&self) -> Result; + fn exec<'a>(&'a self, options: &'a QueryOptions<'a>) -> Result>; } /// An implementation of `PreparedQuery` for internal use @@ -54,11 +58,11 @@ enum SimplePreparedQueryAction { }, } -impl SimplePreparedQuery { - pub(crate) fn new(connection: S, query: &str, options: QueryOptions) -> Result { - let dataset = DatasetView::new(connection, options.default_graph_as_union); +impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { + 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: _, @@ -112,35 +116,67 @@ impl SimplePreparedQuery { } })) } + + /// Builds SimplePreparedQuery from an existing `GraphPattern`. This is used to support federated queries via `SERVICE` clauses + pub(crate) fn new_from_pattern( + connection: S, + pattern: &GraphPattern, + base_iri: Option<&'a str> + ) -> Result { + let dataset = DatasetView::new(connection); + let (plan, variables) = PlanBuilder::build(dataset.encoder(), pattern)?; + let base_iri = base_iri.map(|str_iri| Iri::parse(str_iri.to_string())); + match base_iri { + Some(Err(_)) => Err(format_err!("Failed to parse base_iri")), + Some(Ok(base_iri)) => + Ok(Self(SimplePreparedQueryAction::Select { + plan, + variables, + evaluator: SimpleEvaluator::new(dataset, Some(base_iri)), + })), + None => + Ok(Self(SimplePreparedQueryAction::Select { + plan, + variables, + evaluator: SimpleEvaluator::new(dataset, None), + })) + } + + } } impl PreparedQuery for SimplePreparedQuery { - fn exec(&self) -> Result> { + fn exec<'a>(&'a self, options: &'a QueryOptions<'a>) -> Result> { match &self.0 { SimplePreparedQueryAction::Select { plan, variables, evaluator, - } => evaluator.evaluate_select_plan(&plan, &variables), + } => evaluator.evaluate_select_plan(&plan, &variables, options), SimplePreparedQueryAction::Ask { plan, evaluator } => { - evaluator.evaluate_ask_plan(&plan) + evaluator.evaluate_ask_plan(&plan, options) } SimplePreparedQueryAction::Construct { plan, construct, evaluator, - } => evaluator.evaluate_construct_plan(&plan, &construct), + } => evaluator.evaluate_construct_plan(&plan, &construct, &options), SimplePreparedQueryAction::Describe { plan, evaluator } => { - evaluator.evaluate_describe_plan(&plan) + evaluator.evaluate_describe_plan(&plan, &options) } } } } +pub trait ServiceHandler { + fn handle<'a>(&'a self, node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)>; +} + /// Options for SPARQL query parsing and evaluation like the query base IRI pub struct QueryOptions<'a> { pub(crate) base_iri: Option<&'a str>, pub(crate) default_graph_as_union: bool, + pub(crate) service_handler: Option>, } impl<'a> Default for QueryOptions<'a> { @@ -148,6 +184,7 @@ impl<'a> Default for QueryOptions<'a> { Self { base_iri: None, default_graph_as_union: false, + service_handler: None as Option>, } } } @@ -164,6 +201,12 @@ impl<'a> QueryOptions<'a> { self.default_graph_as_union = true; self } + + /// Consider the union of all graphs in the repository as the default graph + pub fn with_service_handler(mut self, service_handler: Box) -> Self { + self.service_handler = Some(service_handler); + self + } } /// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) diff --git a/lib/src/sparql/model.rs b/lib/src/sparql/model.rs index 0e1a60dd..81c91f98 100644 --- a/lib/src/sparql/model.rs +++ b/lib/src/sparql/model.rs @@ -116,7 +116,7 @@ pub struct BindingsIterator<'a> { } impl<'a> BindingsIterator<'a> { - pub(crate) fn new( + pub fn new( variables: Vec, iter: Box>>> + 'a>, ) -> Self { diff --git a/lib/src/sparql/plan.rs b/lib/src/sparql/plan.rs index 88d1a912..9b84a8aa 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -1,3 +1,5 @@ +use crate::sparql::GraphPattern; +use crate::sparql::model::Variable; use crate::sparql::eval::StringOrStoreString; use crate::store::numeric_encoder::{ EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup, @@ -16,6 +18,13 @@ pub enum PlanNode { StaticBindings { tuples: Vec, }, + Service { + service_name: PatternValue, + variables: Vec, + child: Box, + graph_pattern: GraphPattern, + silent: bool, + }, QuadPatternJoin { child: Box, subject: PatternValue, @@ -161,6 +170,7 @@ impl PlanNode { set.insert(*position); child.add_variables(set); } + PlanNode::Service { child, .. } => child.add_variables(set), PlanNode::Sort { child, .. } => child.add_variables(set), PlanNode::HashDeduplicate { child } => child.add_variables(set), PlanNode::Skip { child, .. } => child.add_variables(set), @@ -463,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, } } @@ -481,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( @@ -491,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/sparql/plan_builder.rs b/lib/src/sparql/plan_builder.rs index 7b1dbfd5..e75051e4 100644 --- a/lib/src/sparql/plan_builder.rs +++ b/lib/src/sparql/plan_builder.rs @@ -103,11 +103,17 @@ impl PlanBuilder { left: Box::new(self.build_for_graph_pattern(a, variables, graph_name)?), right: Box::new(self.build_for_graph_pattern(b, variables, graph_name)?), }, - GraphPattern::Service(_n, _p, _s) => { - return Err(format_err!( - "SPARQL SERVICE clauses are not implemented yet" - )) - } + GraphPattern::Service(n, p, s) => { + let service_name = self.pattern_value_from_named_node_or_variable(n, variables)?; + let graph_pattern = *p.clone(); + PlanNode::Service { + service_name, + variables: variables.clone(), + child: Box::new(self.build_for_graph_pattern(p, variables, service_name)?), + graph_pattern, + silent: *s, + } + }, GraphPattern::AggregateJoin(GroupPattern(key, p), aggregates) => { let mut inner_variables = key.clone(); let inner_graph_name = diff --git a/lib/src/store/memory.rs b/lib/src/store/memory.rs index d9cbe5cd..55ffac64 100644 --- a/lib/src/store/memory.rs +++ b/lib/src/store/memory.rs @@ -29,8 +29,9 @@ use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}; /// assert_eq!(vec![quad], results.unwrap()); /// /// // SPARQL query -/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); -/// let results = prepared_query.exec().unwrap(); +/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap(); +/// let options = QueryOptions::default(); +/// let results = prepared_query.exec(&options).unwrap(); /// if let QueryResult::Bindings(results) = results { /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// } diff --git a/lib/src/store/mod.rs b/lib/src/store/mod.rs index e648a824..bcc6cdf3 100644 --- a/lib/src/store/mod.rs +++ b/lib/src/store/mod.rs @@ -5,12 +5,13 @@ pub(crate) mod numeric_encoder; #[cfg(feature = "rocksdb")] mod rocksdb; +pub use crate::sparql::GraphPattern; pub use crate::store::memory::MemoryRepository; #[cfg(feature = "rocksdb")] 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::parser::{QuadsParser, TriplesParser}; @@ -71,8 +72,8 @@ impl From for StoreRepositoryConnection { impl RepositoryConnection for StoreRepositoryConnection { type PreparedQuery = SimplePreparedQuery; - fn prepare_query(&self, query: &str, options: QueryOptions) -> 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>( @@ -96,6 +97,15 @@ impl RepositoryConnection for StoreRepositoryConnection { ) } + + fn prepare_query_from_pattern<'a>( + &'a self, + pattern: &GraphPattern, + base_iri: Option<&'a str> + ) -> Result { + SimplePreparedQuery::new_from_pattern(self.inner.clone(), pattern, base_iri) //TODO: avoid clone + } + fn load_graph( &mut self, reader: impl BufRead, diff --git a/lib/src/store/rocksdb.rs b/lib/src/store/rocksdb.rs index 416855bf..6e5eff52 100644 --- a/lib/src/store/rocksdb.rs +++ b/lib/src/store/rocksdb.rs @@ -40,8 +40,9 @@ use std::str; /// assert_eq!(vec![quad], results.unwrap()); /// /// // SPARQL query -/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); -/// let results = prepared_query.exec().unwrap(); +/// let options = QueryOptions::default(); +/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap(); +/// let results = prepared_query.exec(&options).unwrap(); /// if let QueryResult::Bindings(results) = results { /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// } diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs new file mode 100644 index 00000000..e6971a78 --- /dev/null +++ b/lib/tests/service_test_cases.rs @@ -0,0 +1,240 @@ +use rudf::model::*; +use rudf::{GraphSyntax, Repository, RepositoryConnection, MemoryRepository, Result}; +use rudf::sparql::{BindingsIterator, GraphPattern, PreparedQuery, QueryOptions, QueryResult, ServiceHandler}; +use failure::format_err; +use std::io::BufRead; + + + +#[test] +fn simple_service_test() { + + struct TestServiceHandler; + impl ServiceHandler for TestServiceHandler { + fn handle<'a>(&'a self, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { + fn pattern_handler<'a>(graph_pattern: GraphPattern) -> Result> { + let triples = b" .".as_ref(); + do_pattern(triples, graph_pattern, QueryOptions::default()) + }; + Some(pattern_handler) + } + } + + let query = r#" + SELECT ?s ?p ?o + WHERE + { + SERVICE + { ?s ?p ?o + } + } + "#.to_string(); + + + let options = QueryOptions::default().with_service_handler(Box::new(TestServiceHandler)); + let results = do_query(b"".as_ref(), query, options).unwrap(); + let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); + let solution = vec![ + vec![ Some(ex(String::from("s"))), Some(ex(String::from("p"))), Some(ex(String::from("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) -> Option<(fn(GraphPattern) -> Result>)> { + let service1 = NamedNode::parse("http://service1.org").unwrap(); + let service2 = NamedNode::parse("http://service2.org").unwrap(); + if named_node == service1 { Some(TwoServiceTest::handle_service1) } + else if named_node == service2 { Some(TwoServiceTest::handle_service2) } + else { None } + } + } + + + impl TwoServiceTest { + + fn handle_service1<'a>(graph_pattern: GraphPattern) -> Result> { + let triples = br#" + "Bob" . + "Alice" . + "#.as_ref(); + do_pattern(triples, graph_pattern, QueryOptions::default()) + } + + fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result> { + let triples = br#" + . + . + "#.as_ref(); + do_pattern(triples, graph_pattern, QueryOptions::default()) + } + + } + + let query = r#" + PREFIX foaf: + SELECT ?name ?mbox + WHERE + { + SERVICE + { ?s foaf:name ?name + } + + SERVICE + { ?s foaf:mbox ?mbox + } + } + ORDER BY ?name + "#.to_string(); + + let options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + let results = do_query(b"".as_ref(), query, options).unwrap(); + let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); + let solution = vec![ + vec![ Some(literal("Alice".to_string())), Some(mailto("alice@example.com".to_string())) ], + vec![ Some(literal("Bob".to_string())), Some(mailto("bob@example.com".to_string())) ], + ]; + 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, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { + Some(ServiceTest::handle_service) + } + } + + impl ServiceTest { + fn handle_service<'a>(_graph_pattern: GraphPattern) -> Result> { + Err(format_err!("This is supposed to fail")) + } + } + + let query = r#" + PREFIX foaf: + SELECT ?name ?mbox + WHERE + { + SERVICE SILENT + { ?s foaf:name ?name + } + + } + ORDER BY ?name + "#.to_string(); + + let triples = b"".as_ref(); + let options = QueryOptions::default().with_service_handler(Box::new(ServiceTest)); + let results = do_query(triples, query, options).unwrap(); + let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); + println!("Collected: {:?}", collected); + assert_eq!(collected.len(), 0); +} + +#[test] +fn non_silent_service_test() { + + #[derive(Clone,Copy)] + struct ServiceTest; + impl ServiceHandler for ServiceTest { + fn handle<'a>(&'a self, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { + Some(ServiceTest::handle_service) + } + } + + impl ServiceTest { + fn handle_service<'a>(_graph_pattern: GraphPattern) -> Result> { + Err(format_err!("This is supposed to fail")) + } + } + + let query = r#" + PREFIX foaf: + SELECT ?name + WHERE + { + SERVICE + { ?s foaf:name ?name + } + + } + ORDER BY ?name + "#.to_string(); + + let triples = b"".as_ref(); + let options = QueryOptions::default().with_service_handler(Box::new(ServiceTest)); + let results = do_query(triples, query, options).unwrap(); + let result = results.into_values_iter().next(); + match result { + Some(Err(_)) => assert_eq!(true, true), + _ => assert_eq!(true, false, "This should have been an error since the service fails"), + } +} + +fn ex(id: String) -> Term { + Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", &id)).unwrap()) +} + +fn mailto(id: String) -> Term { + Term::NamedNode(NamedNode::parse(format!("mailto:{}", &id)).unwrap()) +} + +fn literal(str: String) -> Term { + Term::Literal(Literal::new_simple_literal(str)) +} + +fn make_repository(reader: impl BufRead) -> Result { + let repository = MemoryRepository::default(); + let mut connection = repository.connection()?; + connection.load_graph(reader, GraphSyntax::NTriples, None, None).unwrap(); + Ok(repository) +} + +fn query_repository<'a>(repository: MemoryRepository, query: String, options: QueryOptions<'a>) -> Result> { + let connection = repository.connection()?; + let prepared_query = connection.prepare_query(&query, None)?; + let result = prepared_query.exec(&options)?; + match result { + QueryResult::Bindings(iterator) => { + let (varaibles, iter) = iterator.destruct(); + let collected = iter.collect::>(); + Ok(BindingsIterator::new(varaibles, Box::new(collected.into_iter()))) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } +} + +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, None)?; + let result = prepared_query.exec(&options)?; + match result { + QueryResult::Bindings(iterator) => { + let (varaibles, iter) = iterator.destruct(); + let collected = iter.collect::>(); + Ok(BindingsIterator::new(varaibles, Box::new(collected.into_iter()))) + } + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } +} + +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> { + 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 fcef66c3..d4d0601f 100644 --- a/lib/tests/sparql_test_cases.rs +++ b/lib/tests/sparql_test_cases.rs @@ -158,13 +158,13 @@ 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)?, Some(&test.query)) { Err(error) => Err(format_err!( "Failure to parse query of {} with error: {}", test, error )), - Ok(query) => match query.exec() { + Ok(query) => match query.exec(&QueryOptions::default()) { Err(error) => Err(format_err!( "Failure to execute query of {} with error: {}", test, error diff --git a/server/src/main.rs b/server/src/main.rs index 3c2cd587..5918593a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -148,9 +148,10 @@ fn evaluate_sparql_query( request: &Request, ) -> Response { //TODO: stream - match connection.prepare_query(query, QueryOptions::default()) { + let options = QueryOptions::default(); + match connection.prepare_query(query, None) { Ok(query) => { - let results = query.exec().unwrap(); + let results = query.exec(&options).unwrap(); if let QueryResult::Graph(_) = results { let supported_formats = [ GraphSyntax::NTriples.media_type(),