Fixes SPARQL FILTER evaluation with VALUES and subqueries

pull/171/head
Tpt 3 years ago
parent aa9afe4641
commit 1ddc9a8788
  1. 37
      lib/src/sparql/eval.rs
  2. 4
      lib/src/sparql/model.rs
  3. 39
      lib/src/sparql/plan.rs
  4. 18
      testsuite/oxigraph-tests/sparql/manifest.ttl
  5. 6
      testsuite/oxigraph-tests/sparql/subquery_in_filter_not_exists.rq
  6. 6
      testsuite/oxigraph-tests/sparql/values_in_filter_exists.rq
  7. 13
      testsuite/oxigraph-tests/sparql/values_in_filter_exists.srx
  8. 6
      testsuite/oxigraph-tests/sparql/values_in_filter_not_exists.rq

@ -108,7 +108,15 @@ impl SimpleEvaluator {
PlanNode::Init => Rc::new(move |from| Box::new(once(Ok(from)))),
PlanNode::StaticBindings { tuples } => {
let tuples = tuples.clone();
Rc::new(move |_| Box::new(tuples.clone().into_iter().map(Ok)))
Rc::new(move |from| {
Box::new(
tuples
.iter()
.filter_map(move |t| Some(Ok(t.combine_with(&from)?)))
.collect::<Vec<_>>()
.into_iter(),
)
})
}
PlanNode::Service {
variables,
@ -473,18 +481,23 @@ impl SimpleEvaluator {
let mapping = mapping.clone();
Rc::new(move |from| {
let mapping = mapping.clone();
Box::new(
child(EncodedTuple::with_capacity(mapping.len())).map(move |tuple| {
let tuple = tuple?;
let mut output_tuple = EncodedTuple::with_capacity(from.capacity());
for (input_key, output_key) in mapping.iter() {
if let Some(value) = tuple.get(*input_key) {
output_tuple.set(*output_key, value.clone());
}
// We map forward the "from" values to make sure we join wit them
let mut inner_from = EncodedTuple::with_capacity(mapping.len());
for (input_key, output_key) in mapping.iter() {
if let Some(value) = from.get(*output_key) {
inner_from.set(*input_key, value.clone());
}
}
Box::new(child(inner_from).map(move |tuple| {
let tuple = tuple?;
let mut output_tuple = from.clone();
for (input_key, output_key) in mapping.iter() {
if let Some(value) = tuple.get(*input_key) {
output_tuple.set(*output_key, value.clone());
}
Ok(output_tuple)
}),
)
}
Ok(output_tuple)
}))
})
}
PlanNode::Aggregate {

@ -287,7 +287,9 @@ impl QuerySolution {
/// ```
#[inline]
pub fn get(&self, index: impl VariableSolutionIndex) -> Option<&Term> {
self.values.get(index.index(self)?).and_then(std::option::Option::as_ref)
self.values
.get(index.index(self)?)
.and_then(std::option::Option::as_ref)
}
/// The number of variables which could be bound

@ -240,18 +240,9 @@ pub enum PlanExpression {
Floor(Box<Self>),
Round(Box<Self>),
Concat(Vec<Self>),
SubStr(
Box<Self>,
Box<Self>,
Option<Box<Self>>,
),
SubStr(Box<Self>, Box<Self>, Option<Box<Self>>),
StrLen(Box<Self>),
Replace(
Box<Self>,
Box<Self>,
Box<Self>,
Option<Box<Self>>,
),
Replace(Box<Self>, Box<Self>, Box<Self>, Option<Box<Self>>),
UCase(Box<Self>),
LCase(Box<Self>),
EncodeForUri(Box<Self>),
@ -277,11 +268,7 @@ pub enum PlanExpression {
Sha384(Box<Self>),
Sha512(Box<Self>),
Coalesce(Vec<Self>),
If(
Box<Self>,
Box<Self>,
Box<Self>,
),
If(Box<Self>, Box<Self>, Box<Self>),
StrLang(Box<Self>, Box<Self>),
StrDt(Box<Self>, Box<Self>),
SameTerm(Box<Self>, Box<Self>),
@ -289,16 +276,8 @@ pub enum PlanExpression {
IsBlank(Box<Self>),
IsLiteral(Box<Self>),
IsNumeric(Box<Self>),
Regex(
Box<Self>,
Box<Self>,
Option<Box<Self>>,
),
Triple(
Box<Self>,
Box<Self>,
Box<Self>,
),
Regex(Box<Self>, Box<Self>, Option<Box<Self>>),
Triple(Box<Self>, Box<Self>, Box<Self>),
Subject(Box<Self>),
Predicate(Box<Self>),
Object(Box<Self>),
@ -534,8 +513,8 @@ impl EncodedTuple {
let mut result = other.inner.clone();
for (key, self_value) in self.inner.iter().enumerate() {
if let Some(self_value) = self_value {
match other.inner[key] {
Some(ref other_value) => {
match &other.inner[key] {
Some(other_value) => {
if self_value != other_value {
return None;
}
@ -549,8 +528,8 @@ impl EncodedTuple {
let mut result = self.inner.clone();
for (key, other_value) in other.inner.iter().enumerate() {
if let Some(other_value) = other_value {
match self.inner[key] {
Some(ref self_value) => {
match &self.inner[key] {
Some(self_value) => {
if self_value != other_value {
return None;
}

@ -13,6 +13,9 @@
:group_concat_with_null
:single_not_exists
:property_list_path
:values_in_filter_exists
:values_in_filter_not_exists
:subquery_in_filter_not_exists
) .
:describe rdf:type mf:QueryEvaluationTest ;
@ -47,3 +50,18 @@
:property_list_path rdf:type mf:PositiveSyntaxTest ;
mf:name "PropertyListPathNotEmpty children should be ObjectListPath for consistency" ;
mf:action <property_list_path.rq> .
:values_in_filter_exists rdf:type mf:QueryEvaluationTest ;
mf:name "VALUES inside of FILTER EXISTS" ;
mf:action [ qt:query <values_in_filter_exists.rq> ] ;
mf:result <values_in_filter_exists.srx> .
:values_in_filter_not_exists rdf:type mf:QueryEvaluationTest ;
mf:name "VALUES inside of FILTER EXISTS" ;
mf:action [ qt:query <values_in_filter_not_exists.rq> ] ;
mf:result <values_in_filter_exists.srx> .
:subquery_in_filter_not_exists rdf:type mf:QueryEvaluationTest ;
mf:name "VALUES inside of FILTER EXISTS" ;
mf:action [ qt:query <subquery_in_filter_not_exists.rq> ] ;
mf:result <values_in_filter_exists.srx> .

@ -0,0 +1,6 @@
PREFIX ex: <http://example.com/>
SELECT ?s WHERE {
VALUES ?s { ex:a ex:b }
FILTER NOT EXISTS { {SELECT ?s WHERE { VALUES ?s { ex:b } }} }
}

@ -0,0 +1,6 @@
PREFIX ex: <http://example.com/>
SELECT ?s WHERE {
VALUES ?s { ex:a ex:b }
FILTER EXISTS { VALUES ?s { ex:a ex:c } }
}

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<sparql xmlns="http://www.w3.org/2005/sparql-results#">
<head>
<variable name="s"/>
</head>
<results>
<result>
<binding name="s">
<uri>http://example.com/a</uri>
</binding>
</result>
</results>
</sparql>

@ -0,0 +1,6 @@
PREFIX ex: <http://example.com/>
SELECT ?s WHERE {
VALUES ?s { ex:a ex:b }
FILTER NOT EXISTS { VALUES ?s { ex:b } }
}
Loading…
Cancel
Save