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 } => {
let eval = self.clone();
Box::new(self.eval_plan(*child, from).filter(move |tuple| {
@ -136,23 +142,12 @@ impl<S: EncodedQuadsStore> SimpleEvaluator<S> {
}
}))
}
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<S: EncodedQuadsStore> SimpleEvaluator<S> {
_ => 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<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::Literal;
use sparql::algebra::*;
use store::encoded::EncodedQuadsStore;
use store::numeric_encoder::EncodedTerm;
@ -27,6 +28,10 @@ pub enum PlanNode {
entry: Box<PlanNode>,
children: Vec<PlanNode>,
},
LeftJoin {
left: Box<PlanNode>,
right: Box<PlanNode>,
},
Extend {
child: Box<PlanNode>,
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,

@ -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<String>,
pub query: Url,
pub data: Option<Url>,
pub graph_data: Vec<Url>,
pub result: Option<Url>,
}
@ -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,
}))
}

Loading…
Cancel
Save