diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 9762279a..43a54713 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -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::>() + .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 { diff --git a/lib/src/sparql/model.rs b/lib/src/sparql/model.rs index 532f8235..ab2d6d1c 100644 --- a/lib/src/sparql/model.rs +++ b/lib/src/sparql/model.rs @@ -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 diff --git a/lib/src/sparql/plan.rs b/lib/src/sparql/plan.rs index 482952af..de7140df 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -240,18 +240,9 @@ pub enum PlanExpression { Floor(Box), Round(Box), Concat(Vec), - SubStr( - Box, - Box, - Option>, - ), + SubStr(Box, Box, Option>), StrLen(Box), - Replace( - Box, - Box, - Box, - Option>, - ), + Replace(Box, Box, Box, Option>), UCase(Box), LCase(Box), EncodeForUri(Box), @@ -277,11 +268,7 @@ pub enum PlanExpression { Sha384(Box), Sha512(Box), Coalesce(Vec), - If( - Box, - Box, - Box, - ), + If(Box, Box, Box), StrLang(Box, Box), StrDt(Box, Box), SameTerm(Box, Box), @@ -289,16 +276,8 @@ pub enum PlanExpression { IsBlank(Box), IsLiteral(Box), IsNumeric(Box), - Regex( - Box, - Box, - Option>, - ), - Triple( - Box, - Box, - Box, - ), + Regex(Box, Box, Option>), + Triple(Box, Box, Box), Subject(Box), Predicate(Box), Object(Box), @@ -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; } diff --git a/testsuite/oxigraph-tests/sparql/manifest.ttl b/testsuite/oxigraph-tests/sparql/manifest.ttl index 2506901c..84fbe799 100644 --- a/testsuite/oxigraph-tests/sparql/manifest.ttl +++ b/testsuite/oxigraph-tests/sparql/manifest.ttl @@ -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 . + +:values_in_filter_exists rdf:type mf:QueryEvaluationTest ; + mf:name "VALUES inside of FILTER EXISTS" ; + mf:action [ qt:query ] ; + mf:result . + +:values_in_filter_not_exists rdf:type mf:QueryEvaluationTest ; + mf:name "VALUES inside of FILTER EXISTS" ; + mf:action [ qt:query ] ; + mf:result . + +:subquery_in_filter_not_exists rdf:type mf:QueryEvaluationTest ; + mf:name "VALUES inside of FILTER EXISTS" ; + mf:action [ qt:query ] ; + mf:result . diff --git a/testsuite/oxigraph-tests/sparql/subquery_in_filter_not_exists.rq b/testsuite/oxigraph-tests/sparql/subquery_in_filter_not_exists.rq new file mode 100644 index 00000000..c0ce09c9 --- /dev/null +++ b/testsuite/oxigraph-tests/sparql/subquery_in_filter_not_exists.rq @@ -0,0 +1,6 @@ +PREFIX ex: + +SELECT ?s WHERE { + VALUES ?s { ex:a ex:b } + FILTER NOT EXISTS { {SELECT ?s WHERE { VALUES ?s { ex:b } }} } +} diff --git a/testsuite/oxigraph-tests/sparql/values_in_filter_exists.rq b/testsuite/oxigraph-tests/sparql/values_in_filter_exists.rq new file mode 100644 index 00000000..aa99c1df --- /dev/null +++ b/testsuite/oxigraph-tests/sparql/values_in_filter_exists.rq @@ -0,0 +1,6 @@ +PREFIX ex: + +SELECT ?s WHERE { + VALUES ?s { ex:a ex:b } + FILTER EXISTS { VALUES ?s { ex:a ex:c } } +} diff --git a/testsuite/oxigraph-tests/sparql/values_in_filter_exists.srx b/testsuite/oxigraph-tests/sparql/values_in_filter_exists.srx new file mode 100644 index 00000000..a1dfa8dc --- /dev/null +++ b/testsuite/oxigraph-tests/sparql/values_in_filter_exists.srx @@ -0,0 +1,13 @@ + + + + + + + + + http://example.com/a + + + + \ No newline at end of file diff --git a/testsuite/oxigraph-tests/sparql/values_in_filter_not_exists.rq b/testsuite/oxigraph-tests/sparql/values_in_filter_not_exists.rq new file mode 100644 index 00000000..1180f96a --- /dev/null +++ b/testsuite/oxigraph-tests/sparql/values_in_filter_not_exists.rq @@ -0,0 +1,6 @@ +PREFIX ex: + +SELECT ?s WHERE { + VALUES ?s { ex:a ex:b } + FILTER NOT EXISTS { VALUES ?s { ex:b } } +}