From c713dcd2ee8dd8c91b8ac57641e290267bc051d5 Mon Sep 17 00:00:00 2001 From: Tpt Date: Thu, 11 Oct 2018 15:50:20 +0200 Subject: [PATCH] Adds support of OPTIONAL --- lib/src/sparql/eval.rs | 99 +++++++++++++++++++++++++++------- lib/src/sparql/plan.rs | 27 +++++++++- lib/tests/sparql_test_cases.rs | 61 +++++++++++++++------ 3 files changed, 149 insertions(+), 38 deletions(-) diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 8c0b87de..7c4116bd 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -124,6 +124,12 @@ impl SimpleEvaluator { }), ) } + PlanNode::LeftJoin { left, right } => Box::new(LeftJoinIterator { + eval: self.clone(), + right_plan: *right, + left_iter: self.eval_plan(*left, from), + current_right_iter: None, + }), PlanNode::Filter { child, expression } => { let eval = self.clone(); Box::new(self.eval_plan(*child, from).filter(move |tuple| { @@ -136,23 +142,12 @@ impl SimpleEvaluator { } })) } - PlanNode::Union { entry, children } => { - //TODO: avoid clones - let eval = self.clone(); - Box::new(self.eval_plan(*entry, from).flat_map(move |tuple| { - let eval = eval.clone(); - let iter: EncodedTuplesIterator = match tuple { - Ok(tuple) => Box::new( - children - .clone() - .into_iter() - .flat_map(move |child| eval.eval_plan(child, tuple.clone())), - ), - Err(error) => Box::new(once(Err(error))), - }; - iter - })) - } + PlanNode::Union { entry, children } => Box::new(UnionIterator { + eval: self.clone(), + children_plan: children, + input_iter: self.eval_plan(*entry, from), + current_iters: Vec::default(), + }), PlanNode::Extend { child, position, @@ -302,7 +297,7 @@ impl SimpleEvaluator { _ => None, }, PlanExpression::Datatype(e) => self.eval_expression(e, tuple)?.datatype(), - PlanExpression::Bound(v) => Some((*v >= tuple.len() && tuple[*v].is_some()).into()), + PlanExpression::Bound(v) => Some((*v < tuple.len() && tuple[*v].is_some()).into()), PlanExpression::IRI(e) => match self.eval_expression(e, tuple)? { EncodedTerm::NamedNode { iri_id } => Some(EncodedTerm::NamedNode { iri_id }), EncodedTerm::SimpleLiteral { value_id } @@ -586,12 +581,76 @@ fn put_pattern_value(selector: &PatternValue, value: EncodedTerm, tuple: &mut En } fn put_value(position: usize, value: EncodedTerm, tuple: &mut EncodedTuple) { - if tuple.len() > position { + if position < tuple.len() { tuple[position] = Some(value) } else { - if tuple.len() < position { + if position > tuple.len() { tuple.resize(position, None); } tuple.push(Some(value)) } } + +struct LeftJoinIterator { + eval: SimpleEvaluator, + right_plan: PlanNode, + left_iter: EncodedTuplesIterator, + current_right_iter: Option, +} + +impl Iterator for LeftJoinIterator { + type Item = Result; + + fn next(&mut self) -> Option> { + if let Some(ref mut right_iter) = self.current_right_iter { + if let Some(tuple) = right_iter.next() { + return Some(tuple); + } + } + match self.left_iter.next()? { + Ok(left_tuple) => { + let mut right_iter = self + .eval + .eval_plan(self.right_plan.clone(), left_tuple.clone()); + match right_iter.next() { + Some(right_tuple) => { + self.current_right_iter = Some(right_iter); + Some(right_tuple) + } + None => Some(Ok(left_tuple)), + } + } + Err(error) => Some(Err(error)), + } + } +} + +struct UnionIterator { + eval: SimpleEvaluator, + children_plan: Vec, + input_iter: EncodedTuplesIterator, + current_iters: Vec, +} + +impl Iterator for UnionIterator { + type Item = Result; + + fn next(&mut self) -> Option> { + while let Some(mut iter) = self.current_iters.pop() { + if let Some(tuple) = iter.next() { + self.current_iters.push(iter); + return Some(tuple); + } + } + match self.input_iter.next()? { + Ok(input_tuple) => { + for plan in &self.children_plan { + self.current_iters + .push(self.eval.eval_plan(plan.clone(), input_tuple.clone())); + } + } + Err(error) => return Some(Err(error)), + } + self.next() + } +} diff --git a/lib/src/sparql/plan.rs b/lib/src/sparql/plan.rs index db49c007..1896fe38 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -1,4 +1,5 @@ use model::vocab::xsd; +use model::Literal; use sparql::algebra::*; use store::encoded::EncodedQuadsStore; use store::numeric_encoder::EncodedTerm; @@ -27,6 +28,10 @@ pub enum PlanNode { entry: Box, children: Vec, }, + LeftJoin { + left: Box, + right: Box, + }, Extend { child: Box, position: usize, @@ -203,7 +208,25 @@ impl<'a, S: EncodedQuadsStore> PlanBuilder<'a, S> { variables, graph_name, )?, - GraphPattern::LeftJoin(a, b, e) => unimplemented!(), + GraphPattern::LeftJoin(a, b, e) => { + let right = Box::new(self.build_for_graph_pattern( + b, + PlanNode::Init, + variables, + graph_name, + )?); + PlanNode::LeftJoin { + left: Box::new(self.build_for_graph_pattern(a, input, variables, graph_name)?), + right: if *e == Expression::from(Literal::from(true)) { + right + } else { + Box::new(PlanNode::Filter { + child: right, + expression: self.build_for_expression(e, variables)?, + }) + }, + } + } GraphPattern::Filter(e, p) => PlanNode::Filter { child: Box::new(self.build_for_graph_pattern(p, input, variables, graph_name)?), expression: self.build_for_expression(e, variables)?, @@ -220,7 +243,7 @@ impl<'a, S: EncodedQuadsStore> PlanBuilder<'a, S> { stack.push(b); } Some(p) => children.push(self.build_for_graph_pattern( - a, + p, PlanNode::Init, variables, graph_name, diff --git a/lib/tests/sparql_test_cases.rs b/lib/tests/sparql_test_cases.rs index c5cc6a20..531c591a 100644 --- a/lib/tests/sparql_test_cases.rs +++ b/lib/tests/sparql_test_cases.rs @@ -88,6 +88,11 @@ fn sparql_w3c_query_evaluation_testsuite() { Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/cast/manifest.ttl").unwrap(), Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest.ttl") .unwrap(), + Url::parse( + "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/optional-filter/manifest.ttl", + ).unwrap(), + Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/optional/manifest.ttl") + .unwrap(), ]; let test_blacklist = vec![ //Multiple writing of the same xsd:integer. Our system does strong normalization. @@ -97,12 +102,9 @@ fn sparql_w3c_query_evaluation_testsuite() { NamedNode::from_str( "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest#distinct-9", ).unwrap(), - //With LeftJoin + //Test on curly brace scoping with OPTIONAL filter NamedNode::from_str( - "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest#distinct-4", - ).unwrap(), - NamedNode::from_str( - "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest#no-distinct-4", + "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/optional-filter/manifest#dawg-optional-filter-005-not-simplified", ).unwrap(), ]; let client = RDFClient::default(); @@ -130,6 +132,17 @@ fn sparql_w3c_query_evaluation_testsuite() { } None => MemoryDataset::default(), }; + for graph_data in &test.graph_data { + let named_graph = data + .named_graph(&NamedNode::from(graph_data.clone()).into()) + .unwrap(); + client + .load_graph(graph_data.clone()) + .unwrap() + .iter() + .unwrap() + .for_each(|triple| named_graph.insert(&triple.unwrap()).unwrap()); + } match data.query(client.get(&test.query).unwrap()) { Err(error) => assert!( false, @@ -142,14 +155,14 @@ fn sparql_w3c_query_evaluation_testsuite() { .load_sparql_query_result_graph(test.result.clone().unwrap()) .unwrap(); assert!( - actual_graph.is_isomorphic(&expected_graph).unwrap(), - "Failure on {}. Expected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n", - test, - expected_graph, - actual_graph, - client.load_sparql_query(test.query.clone()).unwrap(), - data - ) + actual_graph.is_isomorphic(&expected_graph).unwrap(), + "Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n", + test, + expected_graph, + actual_graph, + client.load_sparql_query(test.query.clone()).unwrap(), + data + ) } } } else { @@ -296,6 +309,7 @@ pub struct Test { pub comment: Option, pub query: Url, pub data: Option, + pub graph_data: Vec, pub result: Option, } @@ -312,6 +326,9 @@ impl fmt::Display for Test { for data in &self.data { write!(f, " with data {}", data)?; } + for data in &self.graph_data { + write!(f, " and graph data {}", data)?; + } for result in &self.result { write!(f, " and expected result {}", result)?; } @@ -371,6 +388,9 @@ pub mod qt { pub static ref DATA: NamedNode = NamedNode::from_str("http://www.w3.org/2001/sw/DataAccess/tests/test-query#data") .unwrap(); + pub static ref GRAPH_DATA: NamedNode = + NamedNode::from_str("http://www.w3.org/2001/sw/DataAccess/tests/test-query#graphData") + .unwrap(); } } @@ -408,12 +428,12 @@ impl<'a> Iterator for TestManifest<'a> { Some(Term::Literal(c)) => Some(c.value().to_string()), _ => None, }; - let (query, data) = match self + let (query, data, graph_data) = match self .graph .object_for_subject_predicate(&test_subject, &*mf::ACTION) .unwrap() { - Some(Term::NamedNode(n)) => (n.into(), None), + Some(Term::NamedNode(n)) => (n.into(), None, vec![]), Some(Term::BlankNode(n)) => { let n = n.into(); let query = match self @@ -433,7 +453,15 @@ impl<'a> Iterator for TestManifest<'a> { Some(Term::NamedNode(q)) => Some(q.into()), _ => None, }; - (query, data) + let graph_data = self + .graph + .objects_for_subject_predicate(&n, &qt::GRAPH_DATA) + .unwrap() + .filter_map(|g| match g { + Ok(Term::NamedNode(q)) => Some(q.into()), + _ => None, + }).collect(); + (query, data, graph_data) } Some(_) => return Some(Err("invalid action".into())), None => { @@ -458,6 +486,7 @@ impl<'a> Iterator for TestManifest<'a> { comment, query, data, + graph_data, result, })) }