From add1dff458f69106c72546e9aa98326db3266b2d Mon Sep 17 00:00:00 2001 From: Tpt Date: Tue, 8 Nov 2022 17:10:20 +0100 Subject: [PATCH] Adds OX_LATERAL OPTIONAL and OX_LATERAL GRAPH --- lib/spargebra/src/algebra.rs | 23 +++++++++++++- lib/spargebra/src/parser.rs | 31 +++++++++++++------ lib/src/sparql/plan_builder.rs | 5 +++ .../oxigraph-tests/sparql/lateral/graph.rq | 6 ++++ .../sparql/lateral/manifest.ttl | 12 +++++++ .../oxigraph-tests/sparql/lateral/optional.rq | 6 ++++ .../oxigraph-tests/sparql/lateral/simple.srx | 17 ++++++++++ 7 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 testsuite/oxigraph-tests/sparql/lateral/graph.rq create mode 100644 testsuite/oxigraph-tests/sparql/lateral/optional.rq create mode 100644 testsuite/oxigraph-tests/sparql/lateral/simple.srx diff --git a/lib/spargebra/src/algebra.rs b/lib/spargebra/src/algebra.rs index ddd3d392..0b445f27 100644 --- a/lib/spargebra/src/algebra.rs +++ b/lib/spargebra/src/algebra.rs @@ -529,6 +529,8 @@ pub enum GraphPattern { right: Box, expression: Option, }, + /// Executes the left child and for each result tuple left join with right by injecting tuple to right + LeftSequence { left: Box, right: Box }, /// [Filter](https://www.w3.org/TR/sparql11-query/#defn_algFilter). Filter { expr: Expression, inner: Box }, /// [Union](https://www.w3.org/TR/sparql11-query/#defn_algUnion). @@ -625,6 +627,13 @@ impl GraphPattern { right.fmt_sse(f)?; write!(f, ")") } + Self::LeftSequence { left, right } => { + write!(f, "(leftsequence ")?; + left.fmt_sse(f)?; + write!(f, " ")?; + right.fmt_sse(f)?; + write!(f, ")") + } Self::LeftJoin { left, right, @@ -846,8 +855,19 @@ impl fmt::Display for GraphPattern { } } Self::Sequence { left, right } => { - write!(f, "{} {}", left, right) + if cfg!(feature = "ex_lateral") && matches!(right.as_ref(), Self::Graph { .. }) { + // The second block might be considered as a modification of the first one. + write!(f, "{} OX_LATERAL {}", left, right) + } else { + write!(f, "{} {}", left, right) + } } + #[cfg(feature = "ex-lateral")] + Self::LeftSequence { left, right } => { + write!(f, "{} OX_LATERAL OPTIONAL {{ {} }}", left, right) + } + #[cfg(not(feature = "ex-lateral"))] + Self::LeftSequence { .. } => Err(fmt::Error), Self::LeftJoin { left, right, @@ -978,6 +998,7 @@ impl GraphPattern { } Self::Join { left, right } | Self::Sequence { left, right } + | Self::LeftSequence { left, right } | Self::LeftJoin { left, right, .. } | Self::Union { left, right } => { left.lookup_in_scope_variables(callback); diff --git a/lib/spargebra/src/parser.rs b/lib/spargebra/src/parser.rs index e7ea423d..5cd4a78f 100644 --- a/lib/spargebra/src/parser.rs +++ b/lib/spargebra/src/parser.rs @@ -363,9 +363,11 @@ enum PartialGraphPattern { Minus(GraphPattern), Bind(Expression, Variable), Filter(Expression), - #[cfg(feature = "ex-lateral")] - Lateral(GraphPattern, Vec), Other(GraphPattern), + #[cfg(feature = "ex-lateral")] + LateralOptional(GraphPattern), + #[cfg(feature = "ex-lateral")] + Lateral(GraphPattern), } fn new_join(l: GraphPattern, r: GraphPattern) -> GraphPattern { @@ -1423,10 +1425,12 @@ parser! { } else { expr }), - #[cfg(feature = "ex-lateral")] - PartialGraphPattern::Lateral(p, mut variables) => - g = new_sequence(g, insert_lateral_variables(p, variables)?), PartialGraphPattern::Other(e) => g = new_join(g, e), + #[cfg(feature = "ex-lateral")] + PartialGraphPattern::LateralOptional(p) => + g = GraphPattern::LeftSequence { left: Box::new(g), right: Box::new(p) }, + #[cfg(feature = "ex-lateral")] + PartialGraphPattern::Lateral(p) => g = new_sequence(g, p), } } @@ -1453,10 +1457,19 @@ parser! { //[56] rule GraphPatternNotTriples() -> PartialGraphPattern = GroupOrUnionGraphPattern() / OptionalGraphPattern() / MinusGraphPattern() / GraphGraphPattern() / ServiceGraphPattern() / Filter() / Bind() / InlineData() / OxLateral() - rule OxLateral() -> PartialGraphPattern = i("OX_LATERAL") _ "(" _ vs:OxLateral_variable()* _ ")" _ "{" _ s:SubSelect() _ "}" {? - #[cfg(feature = "ex-lateral")]{ Ok(PartialGraphPattern::Lateral(s, vs)) } - #[cfg(not(feature = "ex-lateral"))]{ Err("The 'OX_LATERAL' syntax is not enabled") } - } + rule OxLateral() -> PartialGraphPattern = + i("OX_LATERAL") _ i("OPTIONAL") _ p:GroupGraphPattern() {? + #[cfg(feature = "ex-lateral")]{ Ok(PartialGraphPattern::LateralOptional(p)) } + #[cfg(not(feature = "ex-lateral"))]{ Err("The 'OX_LATERAL' syntax is not enabled") } + } / + i("OX_LATERAL") _ i("GRAPH") _ name:VarOrIri() _ p:GroupGraphPattern() {? + #[cfg(feature = "ex-lateral")]{ Ok(PartialGraphPattern::Lateral(GraphPattern::Graph { name, inner: Box::new(p) })) } + #[cfg(not(feature = "ex-lateral"))]{ Err("The 'OX_LATERAL' syntax is not enabled") } + } / + i("OX_LATERAL") _ "(" _ vs:OxLateral_variable()* _ ")" _ "{" _ s:SubSelect() _ "}" {? + #[cfg(feature = "ex-lateral")]{ Ok(PartialGraphPattern::Lateral(insert_lateral_variables(s, vs)?)) } + #[cfg(not(feature = "ex-lateral"))]{ Err("The 'OX_LATERAL' syntax is not enabled") } + } rule OxLateral_variable() -> Variable = v:Var() _ { v } diff --git a/lib/src/sparql/plan_builder.rs b/lib/src/sparql/plan_builder.rs index 401c2be2..e7fa4bfc 100644 --- a/lib/src/sparql/plan_builder.rs +++ b/lib/src/sparql/plan_builder.rs @@ -97,6 +97,11 @@ impl<'a> PlanBuilder<'a> { left: Box::new(self.build_for_graph_pattern(left, variables, graph_name)?), right: Box::new(self.build_for_graph_pattern(right, variables, graph_name)?), }, + GraphPattern::LeftSequence { left, right } => PlanNode::LeftJoin { + left: Box::new(self.build_for_graph_pattern(left, variables, graph_name)?), + right: Box::new(self.build_for_graph_pattern(right, variables, graph_name)?), + possible_problem_vars: Rc::new(Vec::new()), + }, GraphPattern::LeftJoin { left, right, diff --git a/testsuite/oxigraph-tests/sparql/lateral/graph.rq b/testsuite/oxigraph-tests/sparql/lateral/graph.rq new file mode 100644 index 00000000..a9bc85ac --- /dev/null +++ b/testsuite/oxigraph-tests/sparql/lateral/graph.rq @@ -0,0 +1,6 @@ +PREFIX ex: + +SELECT ?s ?o WHERE { + VALUES ?s { ex:S } + OX_LATERAL GRAPH ex:G { FILTER(BOUND(?s)) . VALUES ?o { ex:O } } +} \ No newline at end of file diff --git a/testsuite/oxigraph-tests/sparql/lateral/manifest.ttl b/testsuite/oxigraph-tests/sparql/lateral/manifest.ttl index f3484697..a718e216 100644 --- a/testsuite/oxigraph-tests/sparql/lateral/manifest.ttl +++ b/testsuite/oxigraph-tests/sparql/lateral/manifest.ttl @@ -13,6 +13,8 @@ :subselect_inside_optional :subselect_implicit_aggregate :subselect_explicit_aggregate + :optional + :graph ) . :subselect rdf:type mf:QueryEvaluationTest ; @@ -42,3 +44,13 @@ [ qt:query ; qt:data ] ; mf:result . + +:optional rdf:type mf:QueryEvaluationTest ; + mf:name "OX_LATERAL OPTIONAL test" ; + mf:action [ qt:query ] ; + mf:result . + +:optional rdf:type mf:QueryEvaluationTest ; + mf:name "OX_LATERAL GRAPH test" ; + mf:action [ qt:query ] ; + mf:result . diff --git a/testsuite/oxigraph-tests/sparql/lateral/optional.rq b/testsuite/oxigraph-tests/sparql/lateral/optional.rq new file mode 100644 index 00000000..4203717f --- /dev/null +++ b/testsuite/oxigraph-tests/sparql/lateral/optional.rq @@ -0,0 +1,6 @@ +PREFIX ex: + +SELECT ?s ?o WHERE { + VALUES ?s { ex:S } + OX_LATERAL OPTIONAL { FILTER(BOUND(?s)) . VALUES ?o { ex:O } } +} \ No newline at end of file diff --git a/testsuite/oxigraph-tests/sparql/lateral/simple.srx b/testsuite/oxigraph-tests/sparql/lateral/simple.srx new file mode 100644 index 00000000..e0d761e3 --- /dev/null +++ b/testsuite/oxigraph-tests/sparql/lateral/simple.srx @@ -0,0 +1,17 @@ + + + + + + + + + + http://example.org/S + + + http://example.org/O + + + + \ No newline at end of file