Adds support of OPTIONAL

pull/10/head
Tpt 6 years ago
parent c45e7e40a8
commit c713dcd2ee
  1. 99
      lib/src/sparql/eval.rs
  2. 27
      lib/src/sparql/plan.rs
  3. 61
      lib/tests/sparql_test_cases.rs

@ -124,6 +124,12 @@ impl<S: EncodedQuadsStore> SimpleEvaluator<S> {
}), }),
) )
} }
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 } => { PlanNode::Filter { child, expression } => {
let eval = self.clone(); let eval = self.clone();
Box::new(self.eval_plan(*child, from).filter(move |tuple| { Box::new(self.eval_plan(*child, from).filter(move |tuple| {
@ -136,23 +142,12 @@ impl<S: EncodedQuadsStore> SimpleEvaluator<S> {
} }
})) }))
} }
PlanNode::Union { entry, children } => { PlanNode::Union { entry, children } => Box::new(UnionIterator {
//TODO: avoid clones eval: self.clone(),
let eval = self.clone(); children_plan: children,
Box::new(self.eval_plan(*entry, from).flat_map(move |tuple| { input_iter: self.eval_plan(*entry, from),
let eval = eval.clone(); current_iters: Vec::default(),
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::Extend { PlanNode::Extend {
child, child,
position, position,
@ -302,7 +297,7 @@ impl<S: EncodedQuadsStore> SimpleEvaluator<S> {
_ => None, _ => None,
}, },
PlanExpression::Datatype(e) => self.eval_expression(e, tuple)?.datatype(), 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)? { PlanExpression::IRI(e) => match self.eval_expression(e, tuple)? {
EncodedTerm::NamedNode { iri_id } => Some(EncodedTerm::NamedNode { iri_id }), EncodedTerm::NamedNode { iri_id } => Some(EncodedTerm::NamedNode { iri_id }),
EncodedTerm::SimpleLiteral { value_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) { fn put_value(position: usize, value: EncodedTerm, tuple: &mut EncodedTuple) {
if tuple.len() > position { if position < tuple.len() {
tuple[position] = Some(value) tuple[position] = Some(value)
} else { } else {
if tuple.len() < position { if position > tuple.len() {
tuple.resize(position, None); tuple.resize(position, None);
} }
tuple.push(Some(value)) tuple.push(Some(value))
} }
} }
struct LeftJoinIterator<S: EncodedQuadsStore> {
eval: SimpleEvaluator<S>,
right_plan: PlanNode,
left_iter: EncodedTuplesIterator,
current_right_iter: Option<EncodedTuplesIterator>,
}
impl<S: EncodedQuadsStore> Iterator for LeftJoinIterator<S> {
type Item = Result<EncodedTuple>;
fn next(&mut self) -> Option<Result<EncodedTuple>> {
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<S: EncodedQuadsStore> {
eval: SimpleEvaluator<S>,
children_plan: Vec<PlanNode>,
input_iter: EncodedTuplesIterator,
current_iters: Vec<EncodedTuplesIterator>,
}
impl<S: EncodedQuadsStore> Iterator for UnionIterator<S> {
type Item = Result<EncodedTuple>;
fn next(&mut self) -> Option<Result<EncodedTuple>> {
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()
}
}

@ -1,4 +1,5 @@
use model::vocab::xsd; use model::vocab::xsd;
use model::Literal;
use sparql::algebra::*; use sparql::algebra::*;
use store::encoded::EncodedQuadsStore; use store::encoded::EncodedQuadsStore;
use store::numeric_encoder::EncodedTerm; use store::numeric_encoder::EncodedTerm;
@ -27,6 +28,10 @@ pub enum PlanNode {
entry: Box<PlanNode>, entry: Box<PlanNode>,
children: Vec<PlanNode>, children: Vec<PlanNode>,
}, },
LeftJoin {
left: Box<PlanNode>,
right: Box<PlanNode>,
},
Extend { Extend {
child: Box<PlanNode>, child: Box<PlanNode>,
position: usize, position: usize,
@ -203,7 +208,25 @@ impl<'a, S: EncodedQuadsStore> PlanBuilder<'a, S> {
variables, variables,
graph_name, 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 { GraphPattern::Filter(e, p) => PlanNode::Filter {
child: Box::new(self.build_for_graph_pattern(p, input, variables, graph_name)?), child: Box::new(self.build_for_graph_pattern(p, input, variables, graph_name)?),
expression: self.build_for_expression(e, variables)?, expression: self.build_for_expression(e, variables)?,
@ -220,7 +243,7 @@ impl<'a, S: EncodedQuadsStore> PlanBuilder<'a, S> {
stack.push(b); stack.push(b);
} }
Some(p) => children.push(self.build_for_graph_pattern( Some(p) => children.push(self.build_for_graph_pattern(
a, p,
PlanNode::Init, PlanNode::Init,
variables, variables,
graph_name, graph_name,

@ -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/cast/manifest.ttl").unwrap(),
Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest.ttl") Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest.ttl")
.unwrap(), .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![ let test_blacklist = vec![
//Multiple writing of the same xsd:integer. Our system does strong normalization. //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( NamedNode::from_str(
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest#distinct-9", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest#distinct-9",
).unwrap(), ).unwrap(),
//With LeftJoin //Test on curly brace scoping with OPTIONAL filter
NamedNode::from_str( NamedNode::from_str(
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest#distinct-4", "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/optional-filter/manifest#dawg-optional-filter-005-not-simplified",
).unwrap(),
NamedNode::from_str(
"http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest#no-distinct-4",
).unwrap(), ).unwrap(),
]; ];
let client = RDFClient::default(); let client = RDFClient::default();
@ -130,6 +132,17 @@ fn sparql_w3c_query_evaluation_testsuite() {
} }
None => MemoryDataset::default(), 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()) { match data.query(client.get(&test.query).unwrap()) {
Err(error) => assert!( Err(error) => assert!(
false, false,
@ -142,14 +155,14 @@ fn sparql_w3c_query_evaluation_testsuite() {
.load_sparql_query_result_graph(test.result.clone().unwrap()) .load_sparql_query_result_graph(test.result.clone().unwrap())
.unwrap(); .unwrap();
assert!( assert!(
actual_graph.is_isomorphic(&expected_graph).unwrap(), actual_graph.is_isomorphic(&expected_graph).unwrap(),
"Failure on {}. Expected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n", "Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n",
test, test,
expected_graph, expected_graph,
actual_graph, actual_graph,
client.load_sparql_query(test.query.clone()).unwrap(), client.load_sparql_query(test.query.clone()).unwrap(),
data data
) )
} }
} }
} else { } else {
@ -296,6 +309,7 @@ pub struct Test {
pub comment: Option<String>, pub comment: Option<String>,
pub query: Url, pub query: Url,
pub data: Option<Url>, pub data: Option<Url>,
pub graph_data: Vec<Url>,
pub result: Option<Url>, pub result: Option<Url>,
} }
@ -312,6 +326,9 @@ impl fmt::Display for Test {
for data in &self.data { for data in &self.data {
write!(f, " with data {}", data)?; write!(f, " with data {}", data)?;
} }
for data in &self.graph_data {
write!(f, " and graph data {}", data)?;
}
for result in &self.result { for result in &self.result {
write!(f, " and expected result {}", result)?; write!(f, " and expected result {}", result)?;
} }
@ -371,6 +388,9 @@ pub mod qt {
pub static ref DATA: NamedNode = pub static ref DATA: NamedNode =
NamedNode::from_str("http://www.w3.org/2001/sw/DataAccess/tests/test-query#data") NamedNode::from_str("http://www.w3.org/2001/sw/DataAccess/tests/test-query#data")
.unwrap(); .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()), Some(Term::Literal(c)) => Some(c.value().to_string()),
_ => None, _ => None,
}; };
let (query, data) = match self let (query, data, graph_data) = match self
.graph .graph
.object_for_subject_predicate(&test_subject, &*mf::ACTION) .object_for_subject_predicate(&test_subject, &*mf::ACTION)
.unwrap() .unwrap()
{ {
Some(Term::NamedNode(n)) => (n.into(), None), Some(Term::NamedNode(n)) => (n.into(), None, vec![]),
Some(Term::BlankNode(n)) => { Some(Term::BlankNode(n)) => {
let n = n.into(); let n = n.into();
let query = match self let query = match self
@ -433,7 +453,15 @@ impl<'a> Iterator for TestManifest<'a> {
Some(Term::NamedNode(q)) => Some(q.into()), Some(Term::NamedNode(q)) => Some(q.into()),
_ => None, _ => 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())), Some(_) => return Some(Err("invalid action".into())),
None => { None => {
@ -458,6 +486,7 @@ impl<'a> Iterator for TestManifest<'a> {
comment, comment,
query, query,
data, data,
graph_data,
result, result,
})) }))
} }

Loading…
Cancel
Save