parent
7c0563cb1b
commit
40b10cdabc
@ -0,0 +1,28 @@ |
||||
[package] |
||||
name = "sparopt" |
||||
version = "0.1.0-alpha.1-dev" |
||||
authors = ["Tpt <thomas@pellissier-tanon.fr>"] |
||||
license = "MIT OR Apache-2.0" |
||||
readme = "README.md" |
||||
keywords = ["SPARQL"] |
||||
repository = "https://github.com/oxigraph/oxigraph/tree/main/lib/sparopt" |
||||
homepage = "https://oxigraph.org/" |
||||
description = """ |
||||
A SPARQL optimizer |
||||
""" |
||||
edition = "2021" |
||||
rust-version = "1.60" |
||||
|
||||
[features] |
||||
default = [] |
||||
rdf-star = ["oxrdf/rdf-star", "spargebra/rdf-star"] |
||||
sep-0002 = ["spargebra/sep-0002"] |
||||
sep-0006 = ["spargebra/sep-0006"] |
||||
|
||||
[dependencies] |
||||
oxrdf = { version = "0.2.0-alpha.1-dev", path="../oxrdf" } |
||||
rand = "0.8" |
||||
spargebra = { version = "0.3.0-alpha.1-dev", path="../spargebra" } |
||||
|
||||
[package.metadata.docs.rs] |
||||
all-features = true |
@ -0,0 +1,33 @@ |
||||
sparopt |
||||
======= |
||||
|
||||
[![Latest Version](https://img.shields.io/crates/v/sparopt.svg)](https://crates.io/crates/sparopt) |
||||
[![Released API docs](https://docs.rs/sparopt/badge.svg)](https://docs.rs/sparopt) |
||||
[![Crates.io downloads](https://img.shields.io/crates/d/sparopt)](https://crates.io/crates/sparopt) |
||||
[![actions status](https://github.com/oxigraph/oxigraph/workflows/build/badge.svg)](https://github.com/oxigraph/oxigraph/actions) |
||||
[![Gitter](https://badges.gitter.im/oxigraph/community.svg)](https://gitter.im/oxigraph/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) |
||||
|
||||
sparopt is a work in progress [SPARQL Query](https://www.w3.org/TR/sparql11-query/) optimizer. |
||||
|
||||
It relies on the output of [spargebra](https://crates.io/crates/spargebra). |
||||
|
||||
Support for [SPARQL-star](https://w3c.github.io/rdf-star/cg-spec/2021-12-17.html#sparql-star) is also available behind the `rdf-star` feature. |
||||
|
||||
This crate is intended to be a building piece for SPARQL implementations in Rust like [Oxigraph](https://oxigraph.org). |
||||
|
||||
|
||||
## License |
||||
|
||||
This project is licensed under either of |
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or |
||||
`<http://www.apache.org/licenses/LICENSE-2.0>`) |
||||
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or |
||||
`<http://opensource.org/licenses/MIT>`) |
||||
|
||||
at your option. |
||||
|
||||
|
||||
### Contribution |
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Oxigraph by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,5 @@ |
||||
pub use crate::optimizer::Optimizer; |
||||
|
||||
pub mod algebra; |
||||
mod optimizer; |
||||
mod type_inference; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,451 @@ |
||||
use crate::algebra::{Expression, GraphPattern}; |
||||
use oxrdf::Variable; |
||||
use spargebra::algebra::Function; |
||||
use spargebra::term::{GroundTerm, GroundTermPattern, NamedNodePattern}; |
||||
use std::collections::HashMap; |
||||
use std::ops::{BitAnd, BitOr}; |
||||
|
||||
pub fn infer_graph_pattern_types( |
||||
pattern: &GraphPattern, |
||||
mut types: VariableTypes, |
||||
) -> VariableTypes { |
||||
match pattern { |
||||
GraphPattern::QuadPattern { |
||||
subject, |
||||
predicate, |
||||
object, |
||||
graph_name, |
||||
} => { |
||||
add_ground_term_pattern_types(subject, &mut types, false); |
||||
if let NamedNodePattern::Variable(v) = predicate { |
||||
types.intersect_variable_with(v.clone(), VariableType::NAMED_NODE) |
||||
} |
||||
add_ground_term_pattern_types(object, &mut types, true); |
||||
if let Some(NamedNodePattern::Variable(v)) = graph_name { |
||||
types.intersect_variable_with(v.clone(), VariableType::NAMED_NODE) |
||||
} |
||||
types |
||||
} |
||||
GraphPattern::Path { |
||||
subject, |
||||
object, |
||||
graph_name, |
||||
.. |
||||
} => { |
||||
add_ground_term_pattern_types(subject, &mut types, false); |
||||
add_ground_term_pattern_types(object, &mut types, true); |
||||
if let Some(NamedNodePattern::Variable(v)) = graph_name { |
||||
types.intersect_variable_with(v.clone(), VariableType::NAMED_NODE) |
||||
} |
||||
types |
||||
} |
||||
GraphPattern::Join { left, right, .. } => { |
||||
let mut output_types = infer_graph_pattern_types(left, types.clone()); |
||||
output_types.intersect_with(infer_graph_pattern_types(right, types)); |
||||
output_types |
||||
} |
||||
#[cfg(feature = "sep-0006")] |
||||
GraphPattern::Lateral { left, right } => { |
||||
infer_graph_pattern_types(right, infer_graph_pattern_types(left, types)) |
||||
} |
||||
GraphPattern::LeftJoin { left, right, .. } => { |
||||
let mut right_types = infer_graph_pattern_types(right, types.clone()); //TODO: expression
|
||||
for t in right_types.inner.values_mut() { |
||||
t.undef = true; // Right might be unset
|
||||
} |
||||
let mut output_types = infer_graph_pattern_types(left, types); |
||||
output_types.intersect_with(right_types); |
||||
output_types |
||||
} |
||||
GraphPattern::Minus { left, .. } => infer_graph_pattern_types(left, types), |
||||
GraphPattern::Union { inner } => inner |
||||
.iter() |
||||
.map(|inner| infer_graph_pattern_types(inner, types.clone())) |
||||
.reduce(|mut a, b| { |
||||
a.union_with(b); |
||||
a |
||||
}) |
||||
.unwrap_or_default(), |
||||
GraphPattern::Extend { |
||||
inner, |
||||
variable, |
||||
expression, |
||||
} => { |
||||
let mut types = infer_graph_pattern_types(inner, types); |
||||
types.intersect_variable_with( |
||||
variable.clone(), |
||||
infer_expression_type(expression, &types), |
||||
); |
||||
types |
||||
} |
||||
GraphPattern::Filter { inner, .. } => infer_graph_pattern_types(inner, types), |
||||
GraphPattern::Project { inner, variables } => VariableTypes { |
||||
inner: infer_graph_pattern_types(inner, types) |
||||
.inner |
||||
.into_iter() |
||||
.filter(|(v, _)| variables.contains(v)) |
||||
.collect(), |
||||
}, |
||||
GraphPattern::Distinct { inner } |
||||
| GraphPattern::Reduced { inner } |
||||
| GraphPattern::OrderBy { inner, .. } |
||||
| GraphPattern::Slice { inner, .. } => infer_graph_pattern_types(inner, types), |
||||
GraphPattern::Group { |
||||
inner, |
||||
variables, |
||||
aggregates, |
||||
} => { |
||||
let types = infer_graph_pattern_types(inner, types); |
||||
VariableTypes { |
||||
inner: infer_graph_pattern_types(inner, types) |
||||
.inner |
||||
.into_iter() |
||||
.filter(|(v, _)| variables.contains(v)) |
||||
.chain(aggregates.iter().map(|(v, _)| (v.clone(), VariableType::ANY))) //TODO: guess from aggregate
|
||||
.collect(), |
||||
} |
||||
} |
||||
GraphPattern::Values { |
||||
variables, |
||||
bindings, |
||||
} => { |
||||
for (i, v) in variables.iter().enumerate() { |
||||
let mut t = VariableType::default(); |
||||
for binding in bindings { |
||||
match binding[i] { |
||||
Some(GroundTerm::NamedNode(_)) => t.named_node = true, |
||||
Some(GroundTerm::Literal(_)) => t.literal = true, |
||||
#[cfg(feature = "rdf-star")] |
||||
Some(GroundTerm::Triple(_)) => t.triple = true, |
||||
None => t.undef = true, |
||||
} |
||||
} |
||||
types.intersect_variable_with(v.clone(), t) |
||||
} |
||||
types |
||||
} |
||||
GraphPattern::Service { name, inner, .. } => { |
||||
let mut types = infer_graph_pattern_types(inner, types); |
||||
if let NamedNodePattern::Variable(v) = name { |
||||
types.intersect_variable_with(v.clone(), VariableType::NAMED_NODE) |
||||
} |
||||
types |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn add_ground_term_pattern_types( |
||||
pattern: &GroundTermPattern, |
||||
types: &mut VariableTypes, |
||||
is_object: bool, |
||||
) { |
||||
if let GroundTermPattern::Variable(v) = pattern { |
||||
types.intersect_variable_with( |
||||
v.clone(), |
||||
if is_object { |
||||
VariableType::TERM |
||||
} else { |
||||
VariableType::SUBJECT |
||||
}, |
||||
) |
||||
} |
||||
#[cfg(feature = "rdf-star")] |
||||
if let GroundTermPattern::Triple(t) = pattern { |
||||
add_ground_term_pattern_types(&t.subject, types, false); |
||||
if let NamedNodePattern::Variable(v) = &t.predicate { |
||||
types.intersect_variable_with(v.clone(), VariableType::NAMED_NODE) |
||||
} |
||||
add_ground_term_pattern_types(&t.object, types, true); |
||||
} |
||||
} |
||||
|
||||
pub fn infer_expression_type(expression: &Expression, types: &VariableTypes) -> VariableType { |
||||
match expression { |
||||
Expression::NamedNode(_) => VariableType::NAMED_NODE, |
||||
Expression::Literal(_) | Expression::Exists(_) | Expression::Bound(_) => { |
||||
VariableType::LITERAL |
||||
} |
||||
Expression::Variable(v) => types.get(v), |
||||
Expression::FunctionCall(Function::Datatype | Function::Iri, _) => { |
||||
VariableType::NAMED_NODE | VariableType::UNDEF |
||||
} |
||||
#[cfg(feature = "rdf-star")] |
||||
Expression::FunctionCall(Function::Predicate, _) => { |
||||
VariableType::NAMED_NODE | VariableType::UNDEF |
||||
} |
||||
Expression::FunctionCall(Function::BNode, _) => { |
||||
VariableType::BLANK_NODE | VariableType::UNDEF |
||||
} |
||||
Expression::Or(_) |
||||
| Expression::And(_) |
||||
| Expression::Equal(_, _) |
||||
| Expression::Greater(_, _) |
||||
| Expression::GreaterOrEqual(_, _) |
||||
| Expression::Less(_, _) |
||||
| Expression::LessOrEqual(_, _) |
||||
| Expression::Add(_, _) |
||||
| Expression::Subtract(_, _) |
||||
| Expression::Multiply(_, _) |
||||
| Expression::Divide(_, _) |
||||
| Expression::UnaryPlus(_) |
||||
| Expression::UnaryMinus(_) |
||||
| Expression::Not(_) |
||||
| Expression::FunctionCall( |
||||
Function::Str |
||||
| Function::Lang |
||||
| Function::LangMatches |
||||
| Function::Rand |
||||
| Function::Abs |
||||
| Function::Ceil |
||||
| Function::Floor |
||||
| Function::Round |
||||
| Function::Concat |
||||
| Function::SubStr |
||||
| Function::StrLen |
||||
| Function::Replace |
||||
| Function::UCase |
||||
| Function::LCase |
||||
| Function::EncodeForUri |
||||
| Function::Contains |
||||
| Function::StrStarts |
||||
| Function::StrEnds |
||||
| Function::StrBefore |
||||
| Function::StrAfter |
||||
| Function::Year |
||||
| Function::Month |
||||
| Function::Day |
||||
| Function::Hours |
||||
| Function::Minutes |
||||
| Function::Seconds |
||||
| Function::Timezone |
||||
| Function::Tz |
||||
| Function::Now |
||||
| Function::Uuid |
||||
| Function::StrUuid |
||||
| Function::Md5 |
||||
| Function::Sha1 |
||||
| Function::Sha256 |
||||
| Function::Sha384 |
||||
| Function::Sha512 |
||||
| Function::StrLang |
||||
| Function::StrDt |
||||
| Function::IsIri |
||||
| Function::IsBlank |
||||
| Function::IsLiteral |
||||
| Function::IsNumeric |
||||
| Function::Regex, |
||||
_, |
||||
) => VariableType::LITERAL | VariableType::UNDEF, |
||||
#[cfg(feature = "sep-0002")] |
||||
Expression::FunctionCall(Function::Adjust, _) => { |
||||
VariableType::LITERAL | VariableType::UNDEF |
||||
} |
||||
#[cfg(feature = "rdf-star")] |
||||
Expression::FunctionCall(Function::IsTriple, _) => { |
||||
VariableType::LITERAL | VariableType::UNDEF |
||||
} |
||||
Expression::SameTerm(left, right) => { |
||||
if infer_expression_type(left, types).undef || infer_expression_type(right, types).undef |
||||
{ |
||||
VariableType::LITERAL | VariableType::UNDEF |
||||
} else { |
||||
VariableType::LITERAL |
||||
} |
||||
} |
||||
Expression::If(_, then, els) => { |
||||
infer_expression_type(then, types) | infer_expression_type(els, types) |
||||
} |
||||
Expression::Coalesce(inner) => { |
||||
let mut t = VariableType::UNDEF; |
||||
for e in inner { |
||||
let new = infer_expression_type(e, types); |
||||
t = t | new; |
||||
if !new.undef { |
||||
t.undef = false; |
||||
return t; |
||||
} |
||||
} |
||||
t |
||||
} |
||||
#[cfg(feature = "rdf-star")] |
||||
Expression::FunctionCall(Function::Triple, _) => VariableType::TRIPLE | VariableType::UNDEF, |
||||
#[cfg(feature = "rdf-star")] |
||||
Expression::FunctionCall(Function::Subject, _) => { |
||||
VariableType::SUBJECT | VariableType::UNDEF |
||||
} |
||||
#[cfg(feature = "rdf-star")] |
||||
Expression::FunctionCall(Function::Object, _) => VariableType::TERM | VariableType::UNDEF, |
||||
Expression::FunctionCall(Function::Custom(_), _) => VariableType::ANY, |
||||
} |
||||
} |
||||
|
||||
#[derive(Default, Clone, Debug)] |
||||
pub struct VariableTypes { |
||||
inner: HashMap<Variable, VariableType>, |
||||
} |
||||
|
||||
impl VariableTypes { |
||||
pub fn get(&self, variable: &Variable) -> VariableType { |
||||
self.inner |
||||
.get(variable) |
||||
.copied() |
||||
.unwrap_or(VariableType::UNDEF) |
||||
} |
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Variable, &VariableType)> { |
||||
self.inner.iter() |
||||
} |
||||
|
||||
pub fn intersect_with(&mut self, other: Self) { |
||||
for (v, t) in other.inner { |
||||
self.intersect_variable_with(v, t); |
||||
} |
||||
} |
||||
|
||||
pub fn union_with(&mut self, other: Self) { |
||||
for (v, t) in &mut self.inner { |
||||
if other.get(v).undef { |
||||
t.undef = true; // Might be undefined
|
||||
} |
||||
} |
||||
for (v, mut t) in other.inner { |
||||
self.inner |
||||
.entry(v) |
||||
.and_modify(|ex| *ex = *ex | t) |
||||
.or_insert({ |
||||
t.undef = true; |
||||
t |
||||
}); |
||||
} |
||||
} |
||||
|
||||
fn intersect_variable_with(&mut self, variable: Variable, t: VariableType) { |
||||
let t = self.get(&variable) & t; |
||||
if t != VariableType::UNDEF { |
||||
self.inner.insert(variable, t); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] |
||||
pub struct VariableType { |
||||
pub undef: bool, |
||||
pub named_node: bool, |
||||
pub blank_node: bool, |
||||
pub literal: bool, |
||||
#[cfg(feature = "rdf-star")] |
||||
pub triple: bool, |
||||
} |
||||
|
||||
impl VariableType { |
||||
pub const UNDEF: Self = Self { |
||||
undef: true, |
||||
named_node: false, |
||||
blank_node: false, |
||||
literal: false, |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: false, |
||||
}; |
||||
|
||||
const NAMED_NODE: Self = Self { |
||||
undef: false, |
||||
named_node: true, |
||||
blank_node: false, |
||||
literal: false, |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: false, |
||||
}; |
||||
|
||||
const BLANK_NODE: Self = Self { |
||||
undef: false, |
||||
named_node: false, |
||||
blank_node: true, |
||||
literal: false, |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: false, |
||||
}; |
||||
|
||||
const LITERAL: Self = Self { |
||||
undef: false, |
||||
named_node: false, |
||||
blank_node: false, |
||||
literal: true, |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: false, |
||||
}; |
||||
|
||||
#[cfg(feature = "rdf-star")] |
||||
const TRIPLE: Self = Self { |
||||
undef: false, |
||||
named_node: false, |
||||
blank_node: false, |
||||
literal: false, |
||||
triple: true, |
||||
}; |
||||
|
||||
const SUBJECT: Self = Self { |
||||
undef: false, |
||||
named_node: true, |
||||
blank_node: true, |
||||
literal: false, |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: true, |
||||
}; |
||||
|
||||
const TERM: Self = Self { |
||||
undef: false, |
||||
named_node: true, |
||||
blank_node: true, |
||||
literal: true, |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: true, |
||||
}; |
||||
|
||||
const ANY: Self = Self { |
||||
undef: true, |
||||
named_node: true, |
||||
blank_node: true, |
||||
literal: true, |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: true, |
||||
}; |
||||
} |
||||
|
||||
impl BitOr for VariableType { |
||||
type Output = Self; |
||||
|
||||
fn bitor(self, other: Self) -> Self { |
||||
Self { |
||||
undef: self.undef || other.undef, |
||||
named_node: self.named_node || other.named_node, |
||||
blank_node: self.blank_node || other.blank_node, |
||||
literal: self.literal || other.literal, |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: self.triple || other.triple, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl BitAnd for VariableType { |
||||
type Output = Self; |
||||
|
||||
#[allow(clippy::nonminimal_bool)] |
||||
fn bitand(self, other: Self) -> Self { |
||||
Self { |
||||
undef: self.undef && other.undef, |
||||
named_node: self.named_node && other.named_node |
||||
|| (self.undef && other.named_node) |
||||
|| (self.named_node && other.undef), |
||||
blank_node: self.blank_node && other.blank_node |
||||
|| (self.undef && other.blank_node) |
||||
|| (self.blank_node && other.undef), |
||||
literal: self.literal && other.literal |
||||
|| (self.undef && other.literal) |
||||
|| (self.literal && other.undef), |
||||
#[cfg(feature = "rdf-star")] |
||||
triple: self.triple && other.triple |
||||
|| (self.undef && other.triple) |
||||
|| (self.triple && other.undef), |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@ |
||||
PREFIX ex: <http://example.com/> |
||||
|
||||
SELECT ?s ?o WHERE { |
||||
?m2 ex:p2 ?o . |
||||
?s ex:p1 ?m1 , ?m2 . |
||||
?m1 ex:p2 ?o . |
||||
?s ex:p1prime ?m1 . |
||||
?s a ex:C . |
||||
} |
@ -0,0 +1,10 @@ |
||||
PREFIX ex: <http://example.com/> |
||||
|
||||
SELECT ?s ?o WHERE { |
||||
?s a ex:C . |
||||
?s ex:p1 ?m1 . |
||||
?s ex:p1prime ?m1 . |
||||
?s ex:p1 ?m2 . |
||||
?m2 ex:p2 ?o . |
||||
?m1 ex:p2 ?o . |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?a ?o WHERE { |
||||
?s ?p ?o . |
||||
FILTER(BOUND(?a)) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?a ?o WHERE { |
||||
VALUES () {} |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?s WHERE { |
||||
?s ?p ?o . |
||||
FILTER(BOUND(?s)) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?s WHERE { |
||||
?s ?p ?o |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o WHERE { |
||||
{ ?s ?p ?o } UNION { VALUES () {} } |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o WHERE { |
||||
?s ?p ?o . |
||||
} |
@ -0,0 +1,5 @@ |
||||
SELECT ?s1 ?s2 ?o1 ?o2 WHERE { |
||||
?s1 ?p1 ?o1 . |
||||
?s2 ?p2 ?o2 . |
||||
FILTER(?p1 = ?p2) |
||||
} |
@ -0,0 +1,5 @@ |
||||
SELECT ?s1 ?s2 ?o1 ?o2 WHERE { |
||||
?s1 ?p1 ?o1 . |
||||
?s2 ?p2 ?o2 . |
||||
FILTER(sameTerm(?p2, ?p1)) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?s WHERE { |
||||
?s ?p ?o . |
||||
FILTER(EXISTS { VALUES () {}}) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?s WHERE { |
||||
VALUES() {} |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(false && ?o1 = ?o2) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
VALUES () {} |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(false || ?o1 = ?o2) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o2 = ?o1) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(IF(false, ?o1 = ?o2, ?o1 != ?o2)) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o2 != ?o1) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(IF(true, ?o1 = ?o2, ?o1 != ?o2)) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o2 = ?o1) |
||||
} |
@ -0,0 +1,132 @@ |
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . |
||||
@prefix : <https://github.com/oxigraph/oxigraph/tests/sparql-optimization/manifest#> . |
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . |
||||
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> . |
||||
@prefix ox: <https://github.com/oxigraph/oxigraph/tests#> . |
||||
|
||||
<> rdf:type mf:Manifest ; |
||||
rdfs:label "Oxigraph SPARQL optimization tests" ; |
||||
mf:entries |
||||
( |
||||
:unbound_filter |
||||
:unbound_bind |
||||
:something_or_true |
||||
:true_or_something |
||||
:something_or_false |
||||
:false_or_something |
||||
:something_and_true |
||||
:true_and_something |
||||
:something_and_false |
||||
:false_and_something |
||||
:equal_to_same_term |
||||
:bind_always_true |
||||
:bind_always_false |
||||
:if_always_true |
||||
:if_always_false |
||||
:exists_always_false |
||||
:push_filter |
||||
:push_optional_filter |
||||
:empty_union |
||||
:bgp_join_reordering |
||||
) . |
||||
|
||||
|
||||
:unbound_filter rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "unbound variable in filter" ; |
||||
mf:action <unbound_filter_input.rq> ; |
||||
mf:result <unbound_filter_output.rq> . |
||||
|
||||
:unbound_bind rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "unbound variable in bindr" ; |
||||
mf:action <unbound_bind_input.rq> ; |
||||
mf:result <unbound_bind_output.rq> . |
||||
|
||||
:something_or_true rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "something || true" ; |
||||
mf:action <something_or_true_input.rq> ; |
||||
mf:result <something_or_true_output.rq> . |
||||
|
||||
:true_or_something rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "true || something" ; |
||||
mf:action <true_or_something_input.rq> ; |
||||
mf:result <true_or_something_output.rq> . |
||||
|
||||
:something_or_false rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "something || false" ; |
||||
mf:action <something_or_false_input.rq> ; |
||||
mf:result <something_or_false_output.rq> . |
||||
|
||||
:false_or_something rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "false || something" ; |
||||
mf:action <false_or_something_input.rq> ; |
||||
mf:result <false_or_something_output.rq> . |
||||
|
||||
:something_and_true rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "something && true" ; |
||||
mf:action <something_and_true_input.rq> ; |
||||
mf:result <something_and_true_output.rq> . |
||||
|
||||
:true_and_something rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "true && something" ; |
||||
mf:action <true_and_something_input.rq> ; |
||||
mf:result <true_and_something_output.rq> . |
||||
|
||||
:something_and_false rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "something && false" ; |
||||
mf:action <something_and_false_input.rq> ; |
||||
mf:result <something_and_false_output.rq> . |
||||
|
||||
:false_and_something rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "false && something" ; |
||||
mf:action <false_and_something_input.rq> ; |
||||
mf:result <false_and_something_output.rq> . |
||||
|
||||
:equal_to_same_term a ox:QueryOptimizationTest ; |
||||
mf:name "equal to same term" ; |
||||
mf:action <equal_to_same_term_input.rq> ; |
||||
mf:result <equal_to_same_term_output.rq> . |
||||
|
||||
:bind_always_true rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "BIND() always true" ; |
||||
mf:action <bind_always_true_input.rq> ; |
||||
mf:result <bind_always_true_output.rq> . |
||||
|
||||
:bind_always_false rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "BIND() always false" ; |
||||
mf:action <bind_always_false_input.rq> ; |
||||
mf:result <bind_always_false_output.rq> . |
||||
|
||||
:if_always_true rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "IF() always true" ; |
||||
mf:action <if_always_true_input.rq> ; |
||||
mf:result <if_always_true_output.rq> . |
||||
|
||||
:if_always_false rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "IF() always false" ; |
||||
mf:action <if_always_false_input.rq> ; |
||||
mf:result <if_always_false_output.rq> . |
||||
|
||||
:exists_always_false rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "EXISTS {} always false" ; |
||||
mf:action <exists_always_false_input.rq> ; |
||||
mf:result <exists_always_false_output.rq> . |
||||
|
||||
:push_filter rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "push filter down" ; |
||||
mf:action <push_filter_input.rq> ; |
||||
mf:result <push_filter_output.rq> . |
||||
|
||||
:push_optional_filter rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "push OPTIONAL filter down" ; |
||||
mf:action <push_optional_filter_input.rq> ; |
||||
mf:result <push_optional_filter_output.rq> . |
||||
|
||||
:empty_union rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "empty UNION" ; |
||||
mf:action <empty_union_input.rq> ; |
||||
mf:result <empty_union_output.rq> . |
||||
|
||||
:bgp_join_reordering rdf:type ox:QueryOptimizationTest ; |
||||
mf:name "BGP join reordering" ; |
||||
mf:action <bgp_join_reordering_input.rq> ; |
||||
mf:result <bgp_join_reordering_output.rq> . |
@ -0,0 +1,11 @@ |
||||
PREFIX : <http://example.com/> |
||||
|
||||
SELECT ?o1 ?o2 ?o4 ?o5 WHERE { |
||||
?s :p1 ?o1 ; :p4 ?o4 ; :p5 ?o5 . |
||||
LATERAL { ?s :p2 ?o2 } |
||||
MINUS { ?s :p3 ?o3 } |
||||
FILTER(?o1 = 1) |
||||
FILTER(?o2 = 2) |
||||
FILTER(?o4 = 4) |
||||
FILTER(?o1 = ?o5) |
||||
} |
@ -0,0 +1,18 @@ |
||||
PREFIX : <http://example.com/> |
||||
|
||||
SELECT ?o1 ?o2 ?o4 ?o5 WHERE { |
||||
{ |
||||
{ |
||||
{ |
||||
{ ?s :p1 ?o1 FILTER(1 = ?o1) } |
||||
LATERAL { ?s :p4 ?o4 } |
||||
FILTER(?o4 = 4) |
||||
} |
||||
LATERAL { ?s :p5 ?o5 } |
||||
FILTER(?o5 = ?o1) |
||||
} |
||||
LATERAL { ?s :p2 ?o2 } |
||||
FILTER(?o2 = 2) |
||||
} |
||||
MINUS { ?s :p3 ?o3 } |
||||
} |
@ -0,0 +1,5 @@ |
||||
SELECT ?s ?o WHERE { |
||||
?s a ?t . |
||||
OPTIONAL { { ?s ?p ?o } UNION { ?s ?p ?o2 } FILTER(?o = 1) } |
||||
OPTIONAL { { ?s ?p ?o } UNION { ?s ?p2 ?o2 } FILTER(?o = ?t) } |
||||
} |
@ -0,0 +1,5 @@ |
||||
SELECT ?s ?o WHERE { |
||||
?s a ?t . |
||||
LATERAL { VALUES () {()} OPTIONAL { { ?s ?p ?o FILTER(1 = ?o) } UNION { ?s ?p ?o2 FILTER(1 = ?o) } } } |
||||
LATERAL { VALUES () {()} OPTIONAL { { ?s ?p ?o } UNION { ?s ?p2 ?o2 } FILTER(?t = ?o) } } |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o1 = ?o2 && false) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
VALUES () {} |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o1 = ?o2 && true) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o2 = ?o1) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o1 = ?o2 || false) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o2 = ?o1) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o1 = ?o2 || true) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(true && ?o1 = ?o2) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(?o2 = ?o1) |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
FILTER(true || ?o1 = ?o2) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o1 ?o2 WHERE { |
||||
?s ?p ?o1 , ?o2 . |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o WHERE { |
||||
BIND(?a AS ?o) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o WHERE { |
||||
VALUES () { () } |
||||
} |
@ -0,0 +1,4 @@ |
||||
SELECT ?o WHERE { |
||||
?s ?p ?o . |
||||
FILTER(?a) |
||||
} |
@ -0,0 +1,3 @@ |
||||
SELECT ?o WHERE { |
||||
VALUES () {} |
||||
} |
Loading…
Reference in new issue