From 40680956bae29f99f032f6e5ffaf7ac56e77cff9 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Tue, 15 Oct 2019 17:05:51 -0400 Subject: [PATCH 01/13] setup ServiceHandler --- lib/src/sparql/eval.rs | 312 ++++++++++++++++++++++------------------- lib/src/sparql/mod.rs | 20 ++- 2 files changed, 182 insertions(+), 150 deletions(-) diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index c75d12cc..ce8256d2 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; @@ -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,31 +89,41 @@ 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![]), + 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, { @@ -120,7 +136,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { 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), @@ -186,7 +202,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 = @@ -244,7 +260,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 +271,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 +297,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 +313,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 +329,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { input: from, current_iterator: Box::new(empty()), current_plan: 0, + options, }), PlanNode::Extend { child, @@ -319,9 +337,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 +348,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 +361,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 +381,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 +412,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 +444,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)), ); } }); @@ -666,24 +684,25 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { .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 +712,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 +726,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 +754,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 +770,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 +787,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 +813,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 +828,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 +844,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,10 +864,10 @@ 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, @@ -864,7 +883,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 +902,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 +939,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 +953,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 +995,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 +1061,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 +1086,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 +1095,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 +1146,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 +1180,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 +1211,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 +1257,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 +1280,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 +1310,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 +1323,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 +1334,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 +1352,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 +1362,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 +1371,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 +1380,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 +1545,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)?, ) } @@ -1674,15 +1694,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 +1814,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) } @@ -2067,6 +2089,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 +2101,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 +2155,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 +2171,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 +2185,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> { @@ -2230,7 +2254,7 @@ struct DescribeIterator<'a, S: StoreConnection + '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> { diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index dbbbdb6c..be6b0506 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; @@ -19,6 +20,7 @@ use crate::store::StoreConnection; use crate::Result; 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 +29,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 @@ -115,32 +117,37 @@ impl SimplePreparedQuery { } 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 +155,7 @@ impl<'a> Default for QueryOptions<'a> { Self { base_iri: None, default_graph_as_union: false, + service_handler: None as Option>, } } } From aea8dcebb9d6f651521c0c7d50a2b15ca6b1ef53 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Tue, 15 Oct 2019 17:45:35 -0400 Subject: [PATCH 02/13] wiring hooked up --- lib/src/repository.rs | 11 +- lib/src/sparql/eval.rs | 7 ++ lib/src/sparql/mod.rs | 20 ++- lib/src/sparql/model.rs | 2 +- lib/src/sparql/plan.rs | 8 ++ lib/src/sparql/plan_builder.rs | 15 ++- lib/src/store/mod.rs | 14 ++- lib/tests/service_test_cases.rs | 217 ++++++++++++++++++++++++++++++++ lib/tests/sparql_test_cases.rs | 4 +- 9 files changed, 285 insertions(+), 13 deletions(-) create mode 100644 lib/tests/service_test_cases.rs diff --git a/lib/src/repository.rs b/lib/src/repository.rs index 7d1aca4d..273f7f23 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, QueryOptions}; use crate::{DatasetSyntax, GraphSyntax, Result}; use std::io::BufRead; @@ -81,7 +81,7 @@ pub trait RepositoryConnection: Clone { /// 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, options: &'a QueryOptions<'a>) -> Result; /// Retrieves quads with a filter on each quad component /// @@ -112,6 +112,13 @@ 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 ce8256d2..5b9e9a93 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -130,6 +130,13 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { match node { PlanNode::Init => Box::new(once(Ok(from))), PlanNode::StaticBindings { tuples } => Box::new(tuples.iter().cloned().map(Ok)), + PlanNode::Service { + child, + .. + } => { + println!("Service!"); + self.eval_plan(&*child, from, options) + }, PlanNode::QuadPatternJoin { child, subject, diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index be6b0506..8a11e94b 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -19,6 +19,7 @@ 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; @@ -56,8 +57,8 @@ enum SimplePreparedQueryAction { }, } -impl SimplePreparedQuery { - pub(crate) fn new(connection: S, query: &str, options: QueryOptions) -> Result { +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); //TODO avoid inserting terms in the Repository StringStore Ok(Self(match read_sparql_query(query, options.base_iri)? { @@ -114,6 +115,21 @@ impl 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 (plan, variables) = PlanBuilder::build(dataset.encoder(), pattern)?; + Ok(Self(SimplePreparedQueryAction::Select { + plan, + variables, + evaluator: SimpleEvaluator::new(dataset, iri), + })) + } } impl PreparedQuery for SimplePreparedQuery { 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..aafa855b 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -1,3 +1,4 @@ +use crate::sparql::GraphPattern; use crate::sparql::eval::StringOrStoreString; use crate::store::numeric_encoder::{ EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup, @@ -16,6 +17,12 @@ pub enum PlanNode { StaticBindings { tuples: Vec, }, + Service { + service_name: PatternValue, + child: Box, + graph_pattern: GraphPattern, + silent: bool, + }, QuadPatternJoin { child: Box, subject: PatternValue, @@ -161,6 +168,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), diff --git a/lib/src/sparql/plan_builder.rs b/lib/src/sparql/plan_builder.rs index 7b1dbfd5..9223ed57 100644 --- a/lib/src/sparql/plan_builder.rs +++ b/lib/src/sparql/plan_builder.rs @@ -103,11 +103,16 @@ 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, + 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/mod.rs b/lib/src/store/mod.rs index e648a824..03477db7 100644 --- a/lib/src/store/mod.rs +++ b/lib/src/store/mod.rs @@ -5,6 +5,7 @@ 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; @@ -71,7 +72,7 @@ impl From for StoreRepositoryConnection { impl RepositoryConnection for StoreRepositoryConnection { type PreparedQuery = SimplePreparedQuery; - fn prepare_query(&self, query: &str, options: QueryOptions) -> Result> { + fn prepare_query<'a>(&self, query: &str, options: &'a QueryOptions<'a>) -> Result> { SimplePreparedQuery::new(self.inner.clone(), query, options) //TODO: avoid clone } @@ -96,6 +97,17 @@ 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 + } + + + fn load_graph( &mut self, reader: impl BufRead, diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs new file mode 100644 index 00000000..cdc5ef9a --- /dev/null +++ b/lib/tests/service_test_cases.rs @@ -0,0 +1,217 @@ +use rudf::model::*; +use rudf::{GraphSyntax, Repository, RepositoryConnection, MemoryRepository, Result}; +use rudf::sparql::{BindingsIterator, GraphPattern, PreparedQuery, QueryOptions, QueryResult, ServiceHandler}; +use failure::format_err; + +fn ex(id: String) -> Term { + Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", &id)).unwrap()) +} + +fn foaf(id: String) -> Term { + Term::NamedNode(NamedNode::parse(format!("http://xmlns.com/foaf/0.1/{}", &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)) +} + +/* +#[derive(Clone,Copy)] +struct SimpleServiceTest; +impl ServiceHandler for SimpleServiceTest { + fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { + Some(SimpleServiceTest::handle_service) + } +} + +impl SimpleServiceTest { + fn handle_service<'a>(graph_pattern: GraphPattern) -> Result> { + let repository = MemoryRepository::default(); + let mut connection = repository.connection().unwrap(); + let file = b" ."; + connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); + let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap(); + let result = prepared_query.exec(&Some(SimpleServiceTest)).unwrap(); + match result { + QueryResult::Bindings(iterator) => { + let (variables, iter) = iterator.destruct(); + let cloned_iter = iter.collect::>().into_iter(); + let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); + Ok(new_iter) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } + } +} +*/ + +#[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 repository = MemoryRepository::default(); + let mut connection = repository.connection().unwrap(); + let file = b" ."; + connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); + let query_options = QueryOptions::default(); + let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, &query_options).unwrap(); + let result = prepared_query.exec(&query_options).unwrap(); + match result { + QueryResult::Bindings(iterator) => { + let (variables, iter) = iterator.destruct(); + let cloned_iter = iter.collect::>().into_iter(); + let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); + Ok(new_iter) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } + }; + Some(pattern_handler) + } + } + + + + + let repository = MemoryRepository::default(); + let connection = repository.connection().unwrap(); + + let query = r#" + SELECT ?s ?p ?o + WHERE + { + SERVICE + { ?s ?p ?o + } + } + "#; + + + let query_options = QueryOptions::default(); + let prepared_query = connection.prepare_query(query, &query_options).unwrap(); + let results = prepared_query.exec(&query_options).unwrap(); + if let QueryResult::Bindings(results) = results { + 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); + } else { + assert_eq!(true, false); + } +} +/* +#[derive(Clone,Copy)] +struct TwoServiceTest; +impl ServiceHandler for TwoServiceTest { + fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { + println!("Handler called for {:?}", named_node); + 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 repository = MemoryRepository::default(); + let mut connection = repository.connection().unwrap(); + let file = br#" + "Bob" . + "Alice" . + "#; + connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); + let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap(); + let result = prepared_query.exec(&Some(NoneService)).unwrap(); + match result { + QueryResult::Bindings(iterator) => { + let (variables, iter) = iterator.destruct(); + let cloned_iter = iter.collect::>().into_iter(); + let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); + Ok(new_iter) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } + } + + + + + fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result> { + let repository = MemoryRepository::default(); + let mut connection = repository.connection().unwrap(); + let file = br#" + . + . + "#; + connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); + let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap(); + let result = prepared_query.exec(&Some(NoneService)).unwrap(); + match result { + QueryResult::Bindings(iterator) => { + let (variables, iter) = iterator.destruct(); + let cloned_iter = iter.collect::>().into_iter(); + let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); + Ok(new_iter) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } + } + +} + + +#[test] +fn two_service_test() { + let repository = MemoryRepository::default(); + let connection = repository.connection().unwrap(); + + let query = r#" + PREFIX foaf: + SELECT ?name ?mbox + WHERE + { + SERVICE + { ?s foaf:name ?name + } + + SERVICE + { ?s foaf:mbox ?mbox + } + } + ORDER BY ?name + "#; + + let prepared_query = connection.prepare_query(query, None).unwrap(); + let service_handler = Some(TwoServiceTest); + let results = prepared_query.exec(&service_handler).unwrap(); + if let QueryResult::Bindings(results) = results { + let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); + for c in collected.clone() { + println!("{:?}", c); + } + println!("\n\n\n"); + 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())) ], + ]; + println!("Results: {:?}", collected); + assert_eq!(collected, solution); + } else { + assert_eq!(true, false); + } +} +*/ + + diff --git a/lib/tests/sparql_test_cases.rs b/lib/tests/sparql_test_cases.rs index fcef66c3..ea802e60 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)?, &QueryOptions::default().with_base_iri(&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 From 68d067259ccc86296ceb9a372790ed96db228db6 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Tue, 15 Oct 2019 17:54:14 -0400 Subject: [PATCH 03/13] wiring hooked up --- lib/src/sparql/plan.rs | 2 ++ lib/src/sparql/plan_builder.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/src/sparql/plan.rs b/lib/src/sparql/plan.rs index aafa855b..11246f1e 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -1,4 +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, @@ -19,6 +20,7 @@ pub enum PlanNode { }, Service { service_name: PatternValue, + variables: Vec, child: Box, graph_pattern: GraphPattern, silent: bool, diff --git a/lib/src/sparql/plan_builder.rs b/lib/src/sparql/plan_builder.rs index 9223ed57..e75051e4 100644 --- a/lib/src/sparql/plan_builder.rs +++ b/lib/src/sparql/plan_builder.rs @@ -108,6 +108,7 @@ impl PlanBuilder { 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, From 9d83649a08253478e593e97120ed4ef590db0b3c Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Tue, 15 Oct 2019 18:33:38 -0400 Subject: [PATCH 04/13] working --- lib/src/sparql/eval.rs | 100 ++++++++++++++++++++++++- lib/src/sparql/mod.rs | 6 ++ lib/tests/service_test_cases.rs | 126 ++++++++++++++++---------------- 3 files changed, 168 insertions(+), 64 deletions(-) diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 5b9e9a93..bc2410bc 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -131,11 +131,56 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { PlanNode::Init => Box::new(once(Ok(from))), PlanNode::StaticBindings { tuples } => Box::new(tuples.iter().cloned().map(Ok)), PlanNode::Service { - child, + variables, + silent, + service_name, + graph_pattern, .. } => { - println!("Service!"); - self.eval_plan(&*child, from, options) + match &options.service_handler { + None => if *silent { + return Box::new(vec![].into_iter()); + } 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(vec![].into_iter()); + } else { + return Box::new(once(Err(format_err!( + "The handler supplied was unable to evaluate the given service" + )))) as EncodedTuplesIterator<'_>; + }, + Some(term) => { + let named_node = self.dataset.decode_named_node(term).unwrap(); + handler.handle(named_node) + }, + }; + + match pattern_option { + None => if *silent { + return Box::new(vec![].into_iter()); + } 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) => { + let bindings = pattern_fn(graph_pattern.clone()).unwrap(); + 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![], + }) + }, + } + } + } }, PlanNode::QuadPatternJoin { child, @@ -1589,6 +1634,45 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { ) } + 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.clone().to_vec(); + for v in binding_variables.clone() { + if !combined_variables.contains(&v) { + combined_variables.resize(combined_variables.len() + 1, v); + } + } + + println!("binding_variables: {:?}", binding_variables.clone()); + println!("variables: {:?}", variables.clone()); + println!("combined_variables: {:?}", combined_variables.clone()); + println!("\n\n"); + 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 { @@ -1963,6 +2047,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) diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index 8a11e94b..491acb99 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -188,6 +188,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/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs index cdc5ef9a..342342f4 100644 --- a/lib/tests/service_test_cases.rs +++ b/lib/tests/service_test_cases.rs @@ -94,7 +94,7 @@ fn simple_service_test() { "#; - let query_options = QueryOptions::default(); + let query_options = QueryOptions::default().with_service_handler(Box::new(TestServiceHandler)); let prepared_query = connection.prepare_query(query, &query_options).unwrap(); let results = prepared_query.exec(&query_options).unwrap(); if let QueryResult::Bindings(results) = results { @@ -107,73 +107,78 @@ fn simple_service_test() { assert_eq!(true, false); } } -/* -#[derive(Clone,Copy)] -struct TwoServiceTest; -impl ServiceHandler for TwoServiceTest { - fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { - println!("Handler called for {:?}", named_node); - 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 repository = MemoryRepository::default(); - let mut connection = repository.connection().unwrap(); - let file = br#" - "Bob" . - "Alice" . - "#; - connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); - let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap(); - let result = prepared_query.exec(&Some(NoneService)).unwrap(); - match result { - QueryResult::Bindings(iterator) => { - let (variables, iter) = iterator.destruct(); - let cloned_iter = iter.collect::>().into_iter(); - let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); - Ok(new_iter) - }, - _ => Err(format_err!("Excpected bindings but got another QueryResult")) - } - } +#[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>)> { + println!("Handler called for {:?}", named_node); + 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} + } + } - fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result> { - let repository = MemoryRepository::default(); - let mut connection = repository.connection().unwrap(); - let file = br#" - . - . - "#; - connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); - let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap(); - let result = prepared_query.exec(&Some(NoneService)).unwrap(); - match result { - QueryResult::Bindings(iterator) => { - let (variables, iter) = iterator.destruct(); - let cloned_iter = iter.collect::>().into_iter(); - let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); - Ok(new_iter) - }, - _ => Err(format_err!("Excpected bindings but got another QueryResult")) + impl TwoServiceTest { + + fn handle_service1<'a>(graph_pattern: GraphPattern) -> Result> { + let repository = MemoryRepository::default(); + let mut connection = repository.connection().unwrap(); + let file = br#" + "Bob" . + "Alice" . + "#; + connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); + let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, &query_options).unwrap(); + let result = prepared_query.exec(&query_options).unwrap(); + match result { + QueryResult::Bindings(iterator) => { + let (variables, iter) = iterator.destruct(); + let cloned_iter = iter.collect::>().into_iter(); + let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); + Ok(new_iter) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } } - } -} -#[test] -fn two_service_test() { + + fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result> { + let repository = MemoryRepository::default(); + let mut connection = repository.connection().unwrap(); + let file = br#" + . + . + "#; + connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); + let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, &query_options).unwrap(); + let result = prepared_query.exec(&query_options).unwrap(); + match result { + QueryResult::Bindings(iterator) => { + let (variables, iter) = iterator.destruct(); + let cloned_iter = iter.collect::>().into_iter(); + let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); + Ok(new_iter) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } + } + + } + let repository = MemoryRepository::default(); let connection = repository.connection().unwrap(); @@ -193,9 +198,9 @@ fn two_service_test() { ORDER BY ?name "#; - let prepared_query = connection.prepare_query(query, None).unwrap(); - let service_handler = Some(TwoServiceTest); - let results = prepared_query.exec(&service_handler).unwrap(); + let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + let prepared_query = connection.prepare_query(query, &query_options).unwrap(); + let results = prepared_query.exec(&query_options).unwrap(); if let QueryResult::Bindings(results) = results { let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); for c in collected.clone() { @@ -212,6 +217,5 @@ fn two_service_test() { assert_eq!(true, false); } } -*/ From 5d8e63376b027271603be7b2ae4be05a9f4536e0 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 13:31:06 -0400 Subject: [PATCH 05/13] refactored tests --- lib/src/lib.rs | 2 +- lib/src/sparql/eval.rs | 27 ++-- lib/src/store/mod.rs | 2 +- lib/tests/service_test_cases.rs | 222 ++++++++++++-------------------- 4 files changed, 98 insertions(+), 155 deletions(-) diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 69dba29e..52be919a 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -44,7 +44,7 @@ pub use failure::Error; pub type Result = ::std::result::Result; pub use crate::repository::Repository; pub use crate::repository::RepositoryConnection; -pub use crate::store::MemoryRepository; +pub use crate::store::{MemoryRepository, MemoryRepositoryConnection}; #[cfg(feature = "rocksdb")] pub use crate::store::RocksDbRepository; pub use crate::syntax::DatasetSyntax; diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index bc2410bc..e37fbfc5 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -169,14 +169,18 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { )))) as EncodedTuplesIterator<'_>; }, Some(pattern_fn) => { - let bindings = pattern_fn(graph_pattern.clone()).unwrap(); - 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![], - }) + 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) => return Box::new(once(Err(err))) as EncodedTuplesIterator<'_> + } }, } } @@ -1644,17 +1648,12 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { { let mut encoder = self.dataset.encoder(); let (binding_variables, iter) = BindingsIterator::destruct(iter); - let mut combined_variables = variables.clone().to_vec(); + 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); } } - - println!("binding_variables: {:?}", binding_variables.clone()); - println!("variables: {:?}", variables.clone()); - println!("combined_variables: {:?}", combined_variables.clone()); - println!("\n\n"); Box::new(iter.map(move |terms| { let mut encoded_terms = vec![None; combined_variables.len()]; for (i, term_option) in terms?.into_iter().enumerate() { diff --git a/lib/src/store/mod.rs b/lib/src/store/mod.rs index 03477db7..6a082621 100644 --- a/lib/src/store/mod.rs +++ b/lib/src/store/mod.rs @@ -6,7 +6,7 @@ pub(crate) mod numeric_encoder; mod rocksdb; pub use crate::sparql::GraphPattern; -pub use crate::store::memory::MemoryRepository; +pub use crate::store::memory::{MemoryRepository, MemoryRepositoryConnection}; #[cfg(feature = "rocksdb")] pub use crate::store::rocksdb::RocksDbRepository; diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs index 342342f4..ada59976 100644 --- a/lib/tests/service_test_cases.rs +++ b/lib/tests/service_test_cases.rs @@ -2,87 +2,24 @@ 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; -fn ex(id: String) -> Term { - Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", &id)).unwrap()) -} - -fn foaf(id: String) -> Term { - Term::NamedNode(NamedNode::parse(format!("http://xmlns.com/foaf/0.1/{}", &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)) -} - -/* -#[derive(Clone,Copy)] -struct SimpleServiceTest; -impl ServiceHandler for SimpleServiceTest { - fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { - Some(SimpleServiceTest::handle_service) - } -} -impl SimpleServiceTest { - fn handle_service<'a>(graph_pattern: GraphPattern) -> Result> { - let repository = MemoryRepository::default(); - let mut connection = repository.connection().unwrap(); - let file = b" ."; - connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); - let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap(); - let result = prepared_query.exec(&Some(SimpleServiceTest)).unwrap(); - match result { - QueryResult::Bindings(iterator) => { - let (variables, iter) = iterator.destruct(); - let cloned_iter = iter.collect::>().into_iter(); - let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); - Ok(new_iter) - }, - _ => Err(format_err!("Excpected bindings but got another QueryResult")) - } - } -} -*/ #[test] fn simple_service_test() { struct TestServiceHandler; impl ServiceHandler for TestServiceHandler { - fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { + fn handle<'a>(&'a self, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { fn pattern_handler<'a>(graph_pattern: GraphPattern) -> Result> { - let repository = MemoryRepository::default(); - let mut connection = repository.connection().unwrap(); - let file = b" ."; - connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); - let query_options = QueryOptions::default(); - let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, &query_options).unwrap(); - let result = prepared_query.exec(&query_options).unwrap(); - match result { - QueryResult::Bindings(iterator) => { - let (variables, iter) = iterator.destruct(); - let cloned_iter = iter.collect::>().into_iter(); - let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); - Ok(new_iter) - }, - _ => Err(format_err!("Excpected bindings but got another QueryResult")) - } + let triples = b" .".as_ref(); + do_pattern(triples, graph_pattern, QueryOptions::default()) }; Some(pattern_handler) } } - - - - let repository = MemoryRepository::default(); - let connection = repository.connection().unwrap(); - let query = r#" SELECT ?s ?p ?o WHERE @@ -91,26 +28,20 @@ fn simple_service_test() { { ?s ?p ?o } } - "#; + "#.to_string(); - let query_options = QueryOptions::default().with_service_handler(Box::new(TestServiceHandler)); - let prepared_query = connection.prepare_query(query, &query_options).unwrap(); - let results = prepared_query.exec(&query_options).unwrap(); - if let QueryResult::Bindings(results) = results { - let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); - let solution = vec![ + 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); - } else { - assert_eq!(true, false); - } + ]; + assert_eq!(collected, solution); + } - - #[test] fn two_service_test() { @@ -118,12 +49,11 @@ fn two_service_test() { struct TwoServiceTest; impl ServiceHandler for TwoServiceTest { fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { - println!("Handler called for {:?}", named_node); 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} + else { None } } } @@ -131,57 +61,23 @@ fn two_service_test() { impl TwoServiceTest { fn handle_service1<'a>(graph_pattern: GraphPattern) -> Result> { - let repository = MemoryRepository::default(); - let mut connection = repository.connection().unwrap(); - let file = br#" + let triples = br#" "Bob" . "Alice" . - "#; - connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); - let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); - let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, &query_options).unwrap(); - let result = prepared_query.exec(&query_options).unwrap(); - match result { - QueryResult::Bindings(iterator) => { - let (variables, iter) = iterator.destruct(); - let cloned_iter = iter.collect::>().into_iter(); - let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); - Ok(new_iter) - }, - _ => Err(format_err!("Excpected bindings but got another QueryResult")) - } + "#.as_ref(); + do_pattern(triples, graph_pattern, QueryOptions::default()) } - - - fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result> { - let repository = MemoryRepository::default(); - let mut connection = repository.connection().unwrap(); - let file = br#" + let triples = br#" . . - "#; - connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); - let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); - let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, &query_options).unwrap(); - let result = prepared_query.exec(&query_options).unwrap(); - match result { - QueryResult::Bindings(iterator) => { - let (variables, iter) = iterator.destruct(); - let cloned_iter = iter.collect::>().into_iter(); - let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); - Ok(new_iter) - }, - _ => Err(format_err!("Excpected bindings but got another QueryResult")) - } + "#.as_ref(); + do_pattern(triples, graph_pattern, QueryOptions::default()) } } - let repository = MemoryRepository::default(); - let connection = repository.connection().unwrap(); - let query = r#" PREFIX foaf: SELECT ?name ?mbox @@ -196,26 +92,74 @@ fn two_service_test() { } } ORDER BY ?name - "#; - - let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); - let prepared_query = connection.prepare_query(query, &query_options).unwrap(); - let results = prepared_query.exec(&query_options).unwrap(); - if let QueryResult::Bindings(results) = results { - let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::>(); - for c in collected.clone() { - println!("{:?}", c); - } - println!("\n\n\n"); - let solution = vec![ + "#.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())) ], ]; - println!("Results: {:?}", collected); - assert_eq!(collected, solution); - } else { - assert_eq!(true, false); + assert_eq!(collected, solution); +} + +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, &options)?; + 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>>> { +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 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>>> { +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) +} From 2aa9114deb292e911c32376e84c53650a650934d Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 14:01:39 -0400 Subject: [PATCH 06/13] refactored tests --- lib/src/lib.rs | 2 +- lib/src/store/mod.rs | 2 +- lib/tests/service_test_cases.rs | 49 +++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 52be919a..69dba29e 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -44,7 +44,7 @@ pub use failure::Error; pub type Result = ::std::result::Result; pub use crate::repository::Repository; pub use crate::repository::RepositoryConnection; -pub use crate::store::{MemoryRepository, MemoryRepositoryConnection}; +pub use crate::store::MemoryRepository; #[cfg(feature = "rocksdb")] pub use crate::store::RocksDbRepository; pub use crate::syntax::DatasetSyntax; diff --git a/lib/src/store/mod.rs b/lib/src/store/mod.rs index 6a082621..03477db7 100644 --- a/lib/src/store/mod.rs +++ b/lib/src/store/mod.rs @@ -6,7 +6,7 @@ pub(crate) mod numeric_encoder; mod rocksdb; pub use crate::sparql::GraphPattern; -pub use crate::store::memory::{MemoryRepository, MemoryRepositoryConnection}; +pub use crate::store::memory::MemoryRepository; #[cfg(feature = "rocksdb")] pub use crate::store::rocksdb::RocksDbRepository; diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs index ada59976..6a3d6259 100644 --- a/lib/tests/service_test_cases.rs +++ b/lib/tests/service_test_cases.rs @@ -104,6 +104,55 @@ fn two_service_test() { assert_eq!(collected, solution); } +#[test] +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>)> { + Some(TwoServiceTest::handle_service) + } + } + + + impl TwoServiceTest { + 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 + { ?s foaf:name ?name + } + + SERVICE + { ?s foaf:mbox ?mbox + } + } + ORDER BY ?name + "#.to_string(); + + let triples = br#" + "Bob" . + "Alice" . + "#.as_ref(); + + let options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + let results = do_query(triples, 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); +} + fn ex(id: String) -> Term { Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", &id)).unwrap()) } From 0e12edfdc96f052453ee022b9aa3b0fb2476ba50 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 15:27:50 -0400 Subject: [PATCH 07/13] refactored prepare_query --- lib/src/repository.rs | 16 +++--- lib/src/sparql/eval.rs | 96 +++++++++++++++++---------------- lib/src/sparql/mod.rs | 21 ++++---- lib/src/sparql/plan.rs | 7 ++- lib/src/store/mod.rs | 12 ++--- lib/tests/service_test_cases.rs | 9 ++-- lib/tests/sparql_test_cases.rs | 2 +- 7 files changed, 80 insertions(+), 83 deletions(-) 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: {}", From a8ce5653f74f8717d7033c4e32f9388e9ca6c8b9 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 15:30:21 -0400 Subject: [PATCH 08/13] clean up warnings --- lib/src/repository.rs | 3 +-- lib/src/sparql/mod.rs | 8 ++++---- lib/src/store/mod.rs | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/src/repository.rs b/lib/src/repository.rs index 11c35fa8..8f445194 100644 --- a/lib/src/repository.rs +++ b/lib/src/repository.rs @@ -1,7 +1,6 @@ use crate::model::*; -use crate::sparql::{GraphPattern, PreparedQuery, QueryOptions}; +use crate::sparql::{GraphPattern, PreparedQuery}; 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) diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index fa8eae91..520b4441 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -64,7 +64,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { QueryVariants::Select { algebra, dataset: _, - base_iri, + .. } => { let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Select { @@ -76,7 +76,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { QueryVariants::Ask { algebra, dataset: _, - base_iri, + .. } => { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Ask { @@ -88,7 +88,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { construct, algebra, dataset: _, - base_iri, + .. } => { let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Construct { @@ -104,7 +104,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { QueryVariants::Describe { algebra, dataset: _, - base_iri, + .. } => { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Describe { diff --git a/lib/src/store/mod.rs b/lib/src/store/mod.rs index d27d04e8..f87531ee 100644 --- a/lib/src/store/mod.rs +++ b/lib/src/store/mod.rs @@ -14,7 +14,6 @@ use crate::model::*; 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; From 75c786b35092793e6e25eec7f6341dee13926a32 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 17:12:20 -0400 Subject: [PATCH 09/13] working on tests --- lib/src/sparql/eval.rs | 50 +++++++++++++++++------ lib/tests/service_test_cases.rs | 71 +++++++++++++++++++++++---------- lib/tests/sparql_test_cases.rs | 4 +- 3 files changed, 89 insertions(+), 36 deletions(-) diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index b85a2386..c20b7c1b 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -138,7 +138,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { } => { match &options.service_handler { None => if *silent { - return Box::new(vec![].into_iter()); + return Box::new(empty()); } else { return Box::new(once(Err(format_err!( "No handler was supplied to resolve the given service" @@ -147,21 +147,30 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { Some(handler) => { let pattern_option = match get_pattern_value(service_name, &[]) { None => if *silent { - return Box::new(vec![].into_iter()); + 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) => { - let named_node = self.dataset.decode_named_node(term).unwrap(); - handler.handle(named_node) + 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(vec![].into_iter()); + 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" @@ -178,7 +187,13 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { buffered_results: vec![], }) }, - Err(err) => return Box::new(once(Err(err))) as EncodedTuplesIterator<'_> + Err(err) => { + if *silent { + return Box::new(empty()); + } else { + return Box::new(once(Err(err))) as EncodedTuplesIterator<'_> + } + } } }, } @@ -933,13 +948,22 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { _ => None, }?; let iri = self.dataset.get_str(iri_id).ok()??; - 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()?; - Some(EncodedTerm::NamedNode { iri_id }) + match options.base_iri { + None => { + Iri::parse(iri).ok()?; + Some(EncodedTerm::NamedNode { iri_id }) + }, + Some(str_iri) => { + match Iri::parse(str_iri) { + Ok(base_iri) => self.build_named_node(&base_iri.resolve(&iri).ok()?.into_inner()), + _ => { + Iri::parse(iri).ok()?; + Some(EncodedTerm::NamedNode { iri_id }) + } + } + } } + } PlanExpression::BNode(id) => match id { Some(id) => { diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs index 3016a013..9f02b34f 100644 --- a/lib/tests/service_test_cases.rs +++ b/lib/tests/service_test_cases.rs @@ -105,18 +105,17 @@ fn two_service_test() { } #[test] -fn silent_service_test() { +fn silent_service_empty_set_test() { #[derive(Clone,Copy)] - struct TwoServiceTest; - impl ServiceHandler for TwoServiceTest { + struct ServiceTest; + impl ServiceHandler for ServiceTest { fn handle<'a>(&'a self, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { - Some(TwoServiceTest::handle_service) + Some(ServiceTest::handle_service) } } - - impl TwoServiceTest { + impl ServiceTest { fn handle_service<'a>(_graph_pattern: GraphPattern) -> Result> { Err(format_err!("This is supposed to fail")) } @@ -127,30 +126,60 @@ fn silent_service_test() { SELECT ?name ?mbox WHERE { - SERVICE + 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); +} - SERVICE - { ?s foaf:mbox ?mbox +#[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 = br#" - "Bob" . - "Alice" . - "#.as_ref(); - - let options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + 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::>(); - 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); + 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 { diff --git a/lib/tests/sparql_test_cases.rs b/lib/tests/sparql_test_cases.rs index 5555df9e..7da57ec3 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)?, None) + .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(&QueryOptions::default()) { + Ok(query) => match query.exec(&QueryOptions::default().with_base_iri(&test.query)) { Err(error) => Err(format_err!( "Failure to execute query of {} with error: {}", test, error From 5912e4c40b994e9d5d431a687543d3801a3c0c37 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 18:15:22 -0400 Subject: [PATCH 10/13] tests working --- lib/src/lib.rs | 5 ++-- lib/src/repository.rs | 11 +++++---- lib/src/sparql/eval.rs | 27 +++++++++------------- lib/src/sparql/mod.rs | 41 ++++++++++++++++++++++----------- lib/src/store/memory.rs | 5 ++-- lib/src/store/mod.rs | 3 ++- lib/tests/service_test_cases.rs | 2 +- lib/tests/sparql_test_cases.rs | 2 +- 8 files changed, 56 insertions(+), 40 deletions(-) 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 8f445194..b9f92cf6 100644 --- a/lib/src/repository.rs +++ b/lib/src/repository.rs @@ -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,8 +76,9 @@ 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())); /// } @@ -87,6 +89,7 @@ pub trait RepositoryConnection: Clone { 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 c20b7c1b..61dd9e61 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -40,15 +40,17 @@ type EncodedTuplesIterator<'a> = Box> + pub struct SimpleEvaluator { dataset: DatasetView, + base_iri: Option>, bnodes_map: Mutex>, now: DateTime, } impl<'a, S: StoreConnection + 'a> SimpleEvaluator { - pub fn new(dataset: DatasetView) -> Self { + pub fn new(dataset: DatasetView, base_iri: Option>) -> Self { Self { dataset, bnodes_map: Mutex::new(BTreeMap::default()), + base_iri, now: Utc::now().with_timezone(&FixedOffset::east(0)), } } @@ -948,22 +950,15 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { _ => None, }?; let iri = self.dataset.get_str(iri_id).ok()??; - match options.base_iri { - None => { - Iri::parse(iri).ok()?; - Some(EncodedTerm::NamedNode { iri_id }) - }, - Some(str_iri) => { - match Iri::parse(str_iri) { - Ok(base_iri) => self.build_named_node(&base_iri.resolve(&iri).ok()?.into_inner()), - _ => { - Iri::parse(iri).ok()?; - Some(EncodedTerm::NamedNode { iri_id }) - } - } - } + 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()?; + Some(EncodedTerm::NamedNode { iri_id }) } - } PlanExpression::BNode(id) => match id { Some(id) => { diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index 520b4441..93bc6156 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -18,6 +18,8 @@ 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; @@ -64,31 +66,31 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { QueryVariants::Select { algebra, dataset: _, - .. + base_iri, } => { let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Select { plan, variables, - evaluator: SimpleEvaluator::new(dataset), + evaluator: SimpleEvaluator::new(dataset, base_iri), } } QueryVariants::Ask { algebra, dataset: _, - .. + base_iri, } => { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Ask { plan, - evaluator: SimpleEvaluator::new(dataset), + evaluator: SimpleEvaluator::new(dataset, base_iri), } } QueryVariants::Construct { construct, algebra, dataset: _, - .. + base_iri, } => { let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Construct { @@ -98,18 +100,18 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { &construct, variables, )?, - evaluator: SimpleEvaluator::new(dataset), + evaluator: SimpleEvaluator::new(dataset, base_iri), } } QueryVariants::Describe { algebra, dataset: _, - .. + base_iri, } => { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Describe { plan, - evaluator: SimpleEvaluator::new(dataset), + evaluator: SimpleEvaluator::new(dataset, base_iri), } } })) @@ -118,14 +120,27 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { 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)?; - Ok(Self(SimplePreparedQueryAction::Select { - plan, - variables, - evaluator: SimpleEvaluator::new(dataset), - })) + 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), + })) + } + } } 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 f87531ee..bcc6cdf3 100644 --- a/lib/src/store/mod.rs +++ b/lib/src/store/mod.rs @@ -101,8 +101,9 @@ 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) //TODO: avoid clone + SimplePreparedQuery::new_from_pattern(self.inner.clone(), pattern, base_iri) //TODO: avoid clone } fn load_graph( diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs index 9f02b34f..e6971a78 100644 --- a/lib/tests/service_test_cases.rs +++ b/lib/tests/service_test_cases.rs @@ -217,7 +217,7 @@ fn query_repository<'a>(repository: MemoryRepository, query: String, options: Qu 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)?; + let prepared_query = connection.prepare_query_from_pattern(&pattern, None)?; let result = prepared_query.exec(&options)?; match result { QueryResult::Bindings(iterator) => { diff --git a/lib/tests/sparql_test_cases.rs b/lib/tests/sparql_test_cases.rs index 7da57ec3..d4d0601f 100644 --- a/lib/tests/sparql_test_cases.rs +++ b/lib/tests/sparql_test_cases.rs @@ -164,7 +164,7 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> { "Failure to parse query of {} with error: {}", test, error )), - Ok(query) => match query.exec(&QueryOptions::default().with_base_iri(&test.query)) { + Ok(query) => match query.exec(&QueryOptions::default()) { Err(error) => Err(format_err!( "Failure to execute query of {} with error: {}", test, error From e7e7aac99e9a5c14034ac1c81d53acef1434abb6 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 20:21:18 -0400 Subject: [PATCH 11/13] updated web project --- server/src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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(), From b69b4febede3e0f7f5af3bbe309fe46a6065d8ff Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 20:56:36 -0400 Subject: [PATCH 12/13] updated docs --- lib/src/store/rocksdb.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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())); /// } From 5fe7b9e0d3bcefeb0a86d81ca1e72837e49dcc24 Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Wed, 16 Oct 2019 21:47:08 -0400 Subject: [PATCH 13/13] added some docs --- lib/src/repository.rs | 2 +- lib/src/sparql/eval.rs | 1 + lib/src/sparql/mod.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/repository.rs b/lib/src/repository.rs index b9f92cf6..2ee0ad10 100644 --- a/lib/src/repository.rs +++ b/lib/src/repository.rs @@ -85,7 +85,7 @@ pub trait RepositoryConnection: Clone { /// ``` 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, diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 61dd9e61..989dcb90 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -1662,6 +1662,7 @@ 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], diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index 93bc6156..5593b3b7 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -117,6 +117,7 @@ impl<'a, S: StoreConnection + 'a> 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,