parent
2bd5da4a5a
commit
1a4adfc02a
@ -0,0 +1,28 @@ |
||||
[package] |
||||
name = "sparopt" |
||||
version = "0.1.0-alpha.1" |
||||
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.1.5", path="../oxrdf" } |
||||
rand = "0.8" |
||||
spargebra = { version = "0.2.8-dev", path="../spargebra" } |
||||
|
||||
[package.metadata.docs.rs] |
||||
all-features = true |
@ -0,0 +1,32 @@ |
||||
sparopt |
||||
========= |
||||
|
||||
[](https://crates.io/crates/sparopt) |
||||
[](https://docs.rs/sparopt) |
||||
[](https://crates.io/crates/sparopt) |
||||
[](https://github.com/oxigraph/oxigraph/actions) |
||||
[](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/#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,4 @@ |
||||
pub use crate::optimizer::Optimizer; |
||||
|
||||
pub mod algebra; |
||||
mod optimizer; |
@ -0,0 +1,225 @@ |
||||
use crate::algebra::{Expression, GraphPattern}; |
||||
|
||||
#[derive(Default)] |
||||
pub struct Optimizer {} |
||||
|
||||
impl Optimizer { |
||||
pub fn optimize(&mut self, pattern: GraphPattern) -> GraphPattern { |
||||
Self::normalize_pattern(pattern) |
||||
} |
||||
|
||||
fn normalize_pattern(pattern: GraphPattern) -> GraphPattern { |
||||
match pattern { |
||||
GraphPattern::QuadPattern { |
||||
subject, |
||||
predicate, |
||||
object, |
||||
graph_name, |
||||
} => GraphPattern::QuadPattern { |
||||
subject, |
||||
predicate, |
||||
object, |
||||
graph_name, |
||||
}, |
||||
GraphPattern::Path { |
||||
subject, |
||||
path, |
||||
object, |
||||
graph_name, |
||||
} => GraphPattern::Path { |
||||
subject, |
||||
path, |
||||
object, |
||||
graph_name, |
||||
}, |
||||
GraphPattern::Join { left, right } => GraphPattern::join( |
||||
Self::normalize_pattern(*left), |
||||
Self::normalize_pattern(*right), |
||||
), |
||||
GraphPattern::LeftJoin { |
||||
left, |
||||
right, |
||||
expression, |
||||
} => GraphPattern::left_join( |
||||
Self::normalize_pattern(*left), |
||||
Self::normalize_pattern(*right), |
||||
Self::normalize_expression(expression), |
||||
), |
||||
#[cfg(feature = "sep-0006")] |
||||
GraphPattern::Lateral { left, right } => GraphPattern::lateral( |
||||
Self::normalize_pattern(*left), |
||||
Self::normalize_pattern(*right), |
||||
), |
||||
GraphPattern::Filter { inner, expression } => GraphPattern::filter( |
||||
Self::normalize_pattern(*inner), |
||||
Self::normalize_expression(expression), |
||||
), |
||||
GraphPattern::Union { inner } => inner |
||||
.into_iter() |
||||
.map(Self::normalize_pattern) |
||||
.reduce(GraphPattern::union) |
||||
.unwrap_or_else(GraphPattern::empty), |
||||
GraphPattern::Extend { |
||||
inner, |
||||
variable, |
||||
expression, |
||||
} => GraphPattern::extend(Self::normalize_pattern(*inner), variable, expression), |
||||
GraphPattern::Minus { left, right } => GraphPattern::minus( |
||||
Self::normalize_pattern(*left), |
||||
Self::normalize_pattern(*right), |
||||
), |
||||
GraphPattern::Values { |
||||
variables, |
||||
bindings, |
||||
} => GraphPattern::values(variables, bindings), |
||||
GraphPattern::OrderBy { inner, expression } => { |
||||
GraphPattern::order_by(Self::normalize_pattern(*inner), expression) |
||||
} |
||||
GraphPattern::Project { inner, variables } => { |
||||
GraphPattern::project(Self::normalize_pattern(*inner), variables) |
||||
} |
||||
GraphPattern::Distinct { inner } => { |
||||
GraphPattern::distinct(Self::normalize_pattern(*inner)) |
||||
} |
||||
GraphPattern::Reduced { inner } => { |
||||
GraphPattern::reduced(Self::normalize_pattern(*inner)) |
||||
} |
||||
GraphPattern::Slice { |
||||
inner, |
||||
start, |
||||
length, |
||||
} => GraphPattern::slice(Self::normalize_pattern(*inner), start, length), |
||||
GraphPattern::Group { |
||||
inner, |
||||
variables, |
||||
aggregates, |
||||
} => GraphPattern::group(Self::normalize_pattern(*inner), variables, aggregates), |
||||
GraphPattern::Service { |
||||
name, |
||||
inner, |
||||
silent, |
||||
} => GraphPattern::service(Self::normalize_pattern(*inner), name, silent), |
||||
} |
||||
} |
||||
|
||||
fn normalize_expression(expression: Expression) -> Expression { |
||||
match expression { |
||||
Expression::NamedNode(node) => node.into(), |
||||
Expression::Literal(literal) => literal.into(), |
||||
Expression::Variable(variable) => variable.into(), |
||||
Expression::Or(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
match ( |
||||
left.effective_boolean_value(), |
||||
right.effective_boolean_value(), |
||||
) { |
||||
(Some(true), _) | (_, Some(true)) => true.into(), |
||||
(Some(false), Some(false)) => false.into(), |
||||
_ => Expression::Or(Box::new(left), Box::new(right)), |
||||
} |
||||
} |
||||
Expression::And(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
match ( |
||||
left.effective_boolean_value(), |
||||
right.effective_boolean_value(), |
||||
) { |
||||
(Some(false), _) | (_, Some(false)) => false.into(), |
||||
(Some(true), Some(true)) => true.into(), |
||||
_ => Expression::And(Box::new(left), Box::new(right)), |
||||
} |
||||
} |
||||
Expression::Equal(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::Equal(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::SameTerm(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::SameTerm(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::Greater(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::Greater(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::GreaterOrEqual(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::GreaterOrEqual(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::Less(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::Less(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::LessOrEqual(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::LessOrEqual(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::In(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = right.into_iter().map(Self::normalize_expression).collect(); |
||||
Expression::In(Box::new(left), right) |
||||
} |
||||
Expression::Add(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::Add(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::Subtract(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::Subtract(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::Multiply(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::Multiply(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::Divide(left, right) => { |
||||
let left = Self::normalize_expression(*left); |
||||
let right = Self::normalize_expression(*right); |
||||
Expression::Divide(Box::new(left), Box::new(right)) |
||||
} |
||||
Expression::UnaryPlus(inner) => { |
||||
let inner = Self::normalize_expression(*inner); |
||||
Expression::UnaryPlus(Box::new(inner)) |
||||
} |
||||
Expression::UnaryMinus(inner) => { |
||||
let inner = Self::normalize_expression(*inner); |
||||
Expression::UnaryMinus(Box::new(inner)) |
||||
} |
||||
Expression::Not(inner) => { |
||||
let inner = Self::normalize_expression(*inner); |
||||
Expression::Not(Box::new(inner)) |
||||
} |
||||
Expression::Exists(inner) => { |
||||
let inner = Self::normalize_pattern(*inner); |
||||
Expression::Exists(Box::new(inner)) |
||||
} |
||||
Expression::Bound(variable) => Expression::Bound(variable), |
||||
Expression::If(cond, then, els) => { |
||||
let cond = Self::normalize_expression(*cond); |
||||
let then = Self::normalize_expression(*then); |
||||
let els = Self::normalize_expression(*els); |
||||
match cond.effective_boolean_value() { |
||||
Some(true) => then, |
||||
Some(false) => els, |
||||
None => Expression::If(Box::new(cond), Box::new(then), Box::new(els)), |
||||
} |
||||
} |
||||
Expression::Coalesce(inners) => { |
||||
Expression::Coalesce(inners.into_iter().map(Self::normalize_expression).collect()) |
||||
} |
||||
Expression::FunctionCall(name, args) => Expression::FunctionCall( |
||||
name, |
||||
args.into_iter().map(Self::normalize_expression).collect(), |
||||
), |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue