You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by rv...@apache.org on 2013/06/25 21:06:45 UTC
svn commit: r1496593 - in /jena/trunk/jena-arq/src:
main/java/com/hp/hpl/jena/sparql/algebra/optimize/
main/java/com/hp/hpl/jena/sparql/expr/
test/java/com/hp/hpl/jena/sparql/algebra/optimize/
Author: rvesse
Date: Tue Jun 25 19:06:44 2013
New Revision: 1496593
URL: http://svn.apache.org/r1496593
Log:
More test cases for implicit join optimizations along with some bug fixes (JENA-473)
Modified:
jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformFilterImplicitJoin.java
jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformImplicitLeftJoin.java
jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/ExprVar.java
jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestSemanticEquivalence.java
jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestTransformFilters.java
Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformFilterImplicitJoin.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformFilterImplicitJoin.java?rev=1496593&r1=1496592&r2=1496593&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformFilterImplicitJoin.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformFilterImplicitJoin.java Tue Jun 25 19:06:44 2013
@@ -420,7 +420,7 @@ public class TransformFilterImplicitJoin
}
private static Op subst(Op subOp, Var find, Var replace) {
- Op op = Substitute.substitute(subOp, find, replace.asNode());
+ Op op = Substitute.substitute(subOp, find, replace);
return OpAssign.assign(op, find, new ExprVar(replace));
}
}
Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformImplicitLeftJoin.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformImplicitLeftJoin.java?rev=1496593&r1=1496592&r2=1496593&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformImplicitLeftJoin.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/algebra/optimize/TransformImplicitLeftJoin.java Tue Jun 25 19:06:44 2013
@@ -150,9 +150,13 @@ public class TransformImplicitLeftJoin e
} else if (rhsVars.contains(lVar)) {
// Substitute left variable for right variable
op = processFilterWorker(op, lVar, rVar);
- } else {
+ } else if (rhsVars.contains(rVar)) {
// Substitute right variable for left variable
op = processFilterWorker(op, rVar, lVar);
+ } else {
+ // May be hit if trying to apply a sequence of
+ // substitutions
+ return null;
}
} else if (lhsVars.contains(lVar)) {
// Only left variable on RHS
@@ -175,8 +179,16 @@ public class TransformImplicitLeftJoin e
return null;
}
} else {
- // May be hit if trying to apply a sequence of substitutions
- return null;
+ // Neither variable is on LHS
+
+ if (rhsVars.contains(lVar) && rhsVars.contains(rVar)) {
+ // Both variables are on RHS so can substitute one for the
+ // other
+ op = processFilterWorker(op, lVar, rVar);
+ } else {
+ // May be hit if trying to apply a sequence of substitutions
+ return null;
+ }
}
// Re-compute visible RHS vars after each substitution as it may
@@ -184,7 +196,11 @@ public class TransformImplicitLeftJoin e
rhsVars = OpVars.visibleVars(op);
}
- return OpLeftJoin.create(left, op, remaining);
+ if (remaining.size() > 0) {
+ return OpLeftJoin.create(left, op, remaining);
+ } else {
+ return OpLeftJoin.create(left, op, (ExprList) null);
+ }
}
private static Pair<List<Pair<Var, Var>>, ExprList> preprocessFilterImplicitJoin(Op left, Op right, ExprList exprs) {
@@ -199,6 +215,8 @@ public class TransformImplicitLeftJoin e
// Special case for where we've split an && in case the
// remaining condition is also an implicit join that we can
// process
+ // TODO See notes in other preprocess() method for a suggested
+ // improved process for && expressions
if (others != exprsOther.size()) {
int possRemove = others;
others = exprsOther.size();
@@ -206,8 +224,12 @@ public class TransformImplicitLeftJoin e
if (p != null) {
exprsJoins.add(p);
// Two possibilities here:
- // 1 - The other condition was a plain implicit join in which case the size of the expr list won't have changed
- // 2 - The other condition was a nested && which contained an implicit join in which case the size of the expr list will have changed
+ // 1 - The other condition was a plain implicit join in
+ // which case the size of the expr list won't have
+ // changed
+ // 2 - The other condition was a nested && which
+ // contained an implicit join in which case the size of
+ // the expr list will have changed
if (others == exprsOther.size()) {
// Trim the additional condition off the others list
exprsOther = possRemove > 0 ? exprsOther.subList(0, possRemove - 1) : new ExprList();
@@ -216,8 +238,9 @@ public class TransformImplicitLeftJoin e
Expr extra = exprsOther.get(exprsOther.size() - 1);
exprsOther = possRemove > 0 ? exprsOther.subList(0, possRemove - 1) : new ExprList();
exprsOther.add(extra);
-
- // NB - We don't try and handle further nesting which in principle may exist
+
+ // NB - We don't try and handle further nesting
+ // which in principle may exist
}
}
}
@@ -236,9 +259,14 @@ public class TransformImplicitLeftJoin e
ExprFunction2 eq = (ExprFunction2) e;
if (e instanceof E_LogicalAnd) {
+ // TODO A better approach would be to define a general recursive
+ // traversal for && which pulls out all implicit joins and produces
+ // an && tree with all other non-eligible expressions
+
// NB - We only handle a single level of nesting here
- // There is some special case code in the calling function that can attempt to recurse one level further
-
+ // There is some special case code in the calling function that can
+ // attempt to recurse one level further
+
// Is LHS of the && an implicit join?
if (eq.getArg1() instanceof E_Equals || eq.getArg1() instanceof E_SameTerm) {
Pair<Var, Var> p = preprocess(opLeft, opRight, eq.getArg1(), exprsOther);
@@ -441,7 +469,7 @@ public class TransformImplicitLeftJoin e
}
private static Op subst(Op subOp, Var find, Var replace) {
- Op op = Substitute.substitute(subOp, find, replace.asNode());
+ Op op = Substitute.substitute(subOp, find, replace);
return OpAssign.assign(op, find, new ExprVar(replace));
}
}
Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/ExprVar.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/ExprVar.java?rev=1496593&r1=1496592&r2=1496593&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/ExprVar.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/sparql/expr/ExprVar.java Tue Jun 25 19:06:44 2013
@@ -69,14 +69,10 @@ public class ExprVar extends ExprNode
public Expr copySubstitute(Binding binding, boolean foldConstants)
{
Var v = varNode ;
- if ( binding == null || !binding.contains(varNode) )
+ if ( binding == null || !binding.contains(v) )
return new ExprVar(v) ;
- return eval(binding, null) ;
-// catch (VariableNotBoundException ex)
-// {
-// Log.warn(this, "Failed to eval bound variable (was bound earlier!)");
-// throw ex ;
-// }
+ Node v2 = binding.get(v);
+ return v2.isVariable() ? new ExprVar(v2) : eval(binding, null) ;
}
@Override
Modified: jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestSemanticEquivalence.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestSemanticEquivalence.java?rev=1496593&r1=1496592&r2=1496593&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestSemanticEquivalence.java (original)
+++ jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestSemanticEquivalence.java Tue Jun 25 19:06:44 2013
@@ -22,8 +22,10 @@ import com.hp.hpl.jena.query.QueryExecut
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.ResultSetFactory;
+import com.hp.hpl.jena.query.ResultSetFormatter;
import com.hp.hpl.jena.query.ResultSetRewindable;
import com.hp.hpl.jena.rdf.model.ModelFactory;
+import com.hp.hpl.jena.sparql.algebra.Algebra;
import com.hp.hpl.jena.sparql.algebra.Op;
import com.hp.hpl.jena.sparql.algebra.OpVars;
import com.hp.hpl.jena.sparql.core.DatasetGraph;
@@ -34,6 +36,9 @@ import com.hp.hpl.jena.sparql.engine.Res
import com.hp.hpl.jena.sparql.engine.binding.BindingFactory;
import com.hp.hpl.jena.sparql.engine.main.QueryEngineMain;
import com.hp.hpl.jena.sparql.resultset.ResultSetCompare;
+import com.hp.hpl.jena.sparql.resultset.ResultsFormat;
+import com.hp.hpl.jena.sparql.resultset.TextOutput;
+import com.hp.hpl.jena.sparql.serializer.SerializationContext;
import com.hp.hpl.jena.sparql.sse.SSE;
import com.hp.hpl.jena.sparql.util.Symbol;
@@ -56,6 +61,7 @@ public class TestSemanticEquivalence {
Node c = NodeFactory.createURI("http://c");
Node p1 = NodeFactory.createURI("http://p1");
Node p2 = NodeFactory.createURI("http://p2");
+ Node pSelf = NodeFactory.createURI("http://self");
Node o = NodeFactory.createLiteral("object");
DatasetGraph dsg = implJoin.asDatasetGraph();
@@ -64,6 +70,7 @@ public class TestSemanticEquivalence {
dsg.add(Quad.defaultGraphNodeGenerated, b, p1, o);
dsg.add(Quad.defaultGraphNodeGenerated, b, p2, o);
dsg.add(Quad.defaultGraphNodeGenerated, c, p1, o);
+ //dsg.add(Quad.defaultGraphNodeGenerated, a, pSelf, a);
// Currently these optimizations are off by default
Assert.assertFalse(ARQ.isFalse(ARQ.optFilterImplicitJoin));
@@ -121,25 +128,34 @@ public class TestSemanticEquivalence {
@Test
public void implicitLeftJoinEvaluation1() {
String query = "SELECT * WHERE { ?x <http://p1> ?o1 . OPTIONAL { ?y <http://p2> ?o2 . FILTER(?x = ?y) } }";
- test(query, implJoin, ARQ.optFilterImplicitJoin, 3);
+ test(query, implJoin, ARQ.optImplicitLeftJoin, 3);
String alg1 = "(leftjoin (bgp (?x <http://p1> ?o1)) (bgp (?y <http://p2> ?o2)) (= ?x ?y))";
- testAsAlgebra(alg1, implJoin, ARQ.optFilterImplicitJoin, 3);
+ testAsAlgebra(alg1, implJoin, ARQ.optImplicitLeftJoin, 3);
String alg2 = "(leftjoin (bgp (?x <http://p1> ?o1)) (bgp (?y <http://p2> ?o2)) (= ?y ?x))";
- testAsAlgebra(alg2, implJoin, ARQ.optFilterImplicitJoin, 3);
+ testAsAlgebra(alg2, implJoin, ARQ.optImplicitLeftJoin, 3);
}
@Test
public void implicitLeftJoinEvaluation2() {
String query = "SELECT * WHERE { ?x <http://p1> ?o1 . OPTIONAL { ?y <http://p2> ?o2 . FILTER(?x = ?y && ?o1 >= ?o2) } }";
- test(query, implJoin, ARQ.optFilterImplicitJoin, 3);
+ test(query, implJoin, ARQ.optImplicitLeftJoin, 3);
String alg1 = "(leftjoin (bgp (?x <http://p1> ?o1)) (bgp (?y <http://p2> ?o2)) (&& (= ?x ?y)(> ?o1 ?o2)))";
- testAsAlgebra(alg1, implJoin, ARQ.optFilterImplicitJoin, 3);
+ testAsAlgebra(alg1, implJoin, ARQ.optImplicitLeftJoin, 3);
String alg2 = "(leftjoin (bgp (?x <http://p1> ?o1)) (bgp (?y <http://p2> ?o2)) (&& (= ?y ?x)(> ?o1 ?o2)))";
- testAsAlgebra(alg2, implJoin, ARQ.optFilterImplicitJoin, 3);
+ testAsAlgebra(alg2, implJoin, ARQ.optImplicitLeftJoin, 3);
+ }
+
+ @Test
+ public void implicitLeftJoinEvaluation3() {
+ String query = "SELECT * WHERE { ?x ?p ?o . OPTIONAL { ?y ?p1 ?o1 . ?y ?p2 ?z . FILTER(?x = ?y) FILTER(?x = ?z) FILTER(?y = ?z) } }";
+ test(query, implJoin, ARQ.optImplicitLeftJoin, 5);
+
+ String alg1 = "(leftjoin (bgp (?x ?p ?o)) (bgp (?y ?p1 ?o1) (?y ?p2 ?z)) ((= ?x ?y) (= ?x ?z) (= ?y ?z)))";
+ testAsAlgebra(alg1, implJoin, ARQ.optImplicitLeftJoin, 5);
}
/**
@@ -160,6 +176,11 @@ public class TestSemanticEquivalence {
if (!q.isSelectType())
Assert.fail("Only SELECT queries are testable with this method");
+
+ System.out.println(q.toString());
+ Op op = Algebra.compile(q);
+ System.out.println(op.toString());
+ System.out.println(Algebra.optimize(op));
// Track current state
boolean isEnabled = ARQ.isTrue(opt);
@@ -170,6 +191,12 @@ public class TestSemanticEquivalence {
ARQ.set(opt, false);
QueryExecution qe = QueryExecutionFactory.create(q, ds);
ResultSetRewindable rs = ResultSetFactory.makeRewindable(qe.execSelect());
+ if (expected != rs.size()) {
+ System.err.println("Non-optimized results not as expected");
+ TextOutput output = new TextOutput((SerializationContext)null);
+ output.format(System.out, rs);
+ rs.reset();
+ }
Assert.assertEquals(expected, rs.size());
qe.close();
@@ -177,6 +204,12 @@ public class TestSemanticEquivalence {
ARQ.set(opt, true);
QueryExecution qeOpt = QueryExecutionFactory.create(q, ds);
ResultSetRewindable rsOpt = ResultSetFactory.makeRewindable(qeOpt.execSelect());
+ if (expected != rsOpt.size()) {
+ System.err.println("Optimized results not as expected");
+ TextOutput output = new TextOutput((SerializationContext)null);
+ output.format(System.out, rsOpt);
+ rsOpt.reset();
+ }
Assert.assertEquals(expected, rsOpt.size());
qeOpt.close();
@@ -224,6 +257,12 @@ public class TestSemanticEquivalence {
QueryIterator iter = engine.eval(op, ds.asDatasetGraph(), BindingFactory.binding(), ARQ.getContext());
ResultSetRewindable rs = ResultSetFactory.makeRewindable(new ResultSetStream(vars, ModelFactory.createDefaultModel(),
iter));
+ if (expected != rs.size()) {
+ System.err.println("Non-optimized results not as expected");
+ TextOutput output = new TextOutput((SerializationContext)null);
+ output.format(System.out, rs);
+ rs.reset();
+ }
Assert.assertEquals(expected, rs.size());
iter.close();
@@ -233,6 +272,12 @@ public class TestSemanticEquivalence {
QueryIterator iterOpt = engine.eval(op, ds.asDatasetGraph(), BindingFactory.binding(), ARQ.getContext());
ResultSetRewindable rsOpt = ResultSetFactory.makeRewindable(new ResultSetStream(vars, ModelFactory
.createDefaultModel(), iterOpt));
+ if (expected != rsOpt.size()) {
+ System.err.println("Optimized results not as expected");
+ TextOutput output = new TextOutput((SerializationContext)null);
+ output.format(System.out, rsOpt);
+ rsOpt.reset();
+ }
Assert.assertEquals(expected, rsOpt.size());
iterOpt.close();
Modified: jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestTransformFilters.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestTransformFilters.java?rev=1496593&r1=1496592&r2=1496593&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestTransformFilters.java (original)
+++ jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/sparql/algebra/optimize/TestTransformFilters.java Tue Jun 25 19:06:44 2013
@@ -18,20 +18,34 @@
package com.hp.hpl.jena.sparql.algebra.optimize;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.plaf.basic.BasicPanelUI;
+
import org.junit.Assert ;
import junit.framework.JUnit4TestAdapter ;
import org.apache.jena.atlas.lib.StrUtils ;
import org.junit.Test ;
+import com.hp.hpl.jena.graph.NodeFactory;
+import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.sparql.algebra.Op ;
import com.hp.hpl.jena.sparql.algebra.Transform ;
import com.hp.hpl.jena.sparql.algebra.Transformer ;
+import com.hp.hpl.jena.sparql.algebra.op.OpAssign;
import com.hp.hpl.jena.sparql.algebra.op.OpBGP ;
import com.hp.hpl.jena.sparql.algebra.op.OpFilter ;
+import com.hp.hpl.jena.sparql.algebra.op.OpTable;
import com.hp.hpl.jena.sparql.algebra.optimize.TransformExpandOneOf ;
import com.hp.hpl.jena.sparql.algebra.optimize.TransformFilterDisjunction ;
import com.hp.hpl.jena.sparql.algebra.optimize.TransformFilterEquality ;
import com.hp.hpl.jena.sparql.algebra.optimize.TransformFilterPlacement ;
+import com.hp.hpl.jena.sparql.core.BasicPattern;
+import com.hp.hpl.jena.sparql.core.Var;
+import com.hp.hpl.jena.sparql.core.VarExprList;
+import com.hp.hpl.jena.sparql.expr.Expr;
+import com.hp.hpl.jena.sparql.expr.ExprVar;
import com.hp.hpl.jena.sparql.sse.SSE ;
/** Tests of transforms related to filters */
@@ -820,21 +834,22 @@ public class TestTransformFilters
@Test public void implicitLeftJoin13()
{
- // There are some overlapping conditions that cannot be safely optimized
+ // There are some overlapping conditions that are safe to optimize however they may end up introducing
+ // additional unnecessary assign operators
test(
"(leftjoin (bgp (?x ?p ?o)) (bgp (?y ?p1 ?o1) (?y ?p2 ?z)) ((= ?x ?y) (= ?x ?z) (= ?y ?z)))",
t_implicitLeftJoin,
- (String[])null);
-
+ "(leftjoin (bgp (?x ?p ?o)) (assign ((?y ?z)) (assign ((?y ?x) (?z ?x)) (bgp (?x ?p1 ?o1) (?x ?p2 ?x)))))");
+
test(
"(leftjoin (bgp (?x ?p ?o)) (bgp (?y ?p1 ?o1) (?y ?p2 ?z)) ((= ?z ?y) (= ?x ?z) (= ?x ?y)))",
t_implicitLeftJoin,
- (String[])null);
+ "(leftjoin (bgp (?x ?p ?o)) (assign ((?z ?x) (?y ?x)) (assign ((?z ?x)) (bgp (?x ?p1 ?o1) (?x ?p2 ?x)))))");
test(
"(leftjoin (bgp (?x ?p ?o)) (bgp (?y ?p1 ?o1) (?y ?p2 ?z)) ((= ?z ?y) (= ?z ?x) (= ?y ?x)))",
t_implicitLeftJoin,
- (String[])null);
+ "(leftjoin (bgp (?x ?p ?o)) (assign ((?z ?x) (?y ?x)) (assign ((?z ?x)) (bgp (?x ?p1 ?o1) (?x ?p2 ?x)))))");
}
@Test public void implicitLeftJoin14()
@@ -878,6 +893,90 @@ public class TestTransformFilters
(String[])null);
}
+ @Test public void implicitLeftJoin18()
+ {
+ // The optimizer is capable of going one level into a nested && to find conditions to apply
+ test(
+ "(leftjoin (bgp (?x ?p ?o)) (bgp (?y ?p1 ?o1) (?y ?p2 ?z)) ((&& (&& (= ?x ?y) (= ?y ?z)) (= ?x ?z))))",
+ t_implicitLeftJoin,
+ "(leftjoin (bgp (?x ?p ?o)) (assign ((?z ?x) (?y ?x)) (bgp (?x ?p1 ?o1) (?x ?p2 ?x))) (= ?y ?z))");
+ }
+
+ @Test public void implicitLeftJoin19()
+ {
+ // Covers the case of both variables on left and neither on right
+ test(
+ "(leftjoin (bgp (?x ?p ?o)(?x <http://pred> ?y)) (table unit) ((= ?x ?y)))",
+ t_implicitLeftJoin,
+ (String[])null);
+
+ // Swapping the order of the equality expression should make no difference
+ test(
+ "(leftjoin (bgp (?x ?p ?o)(?x <http://pred> ?y)) (table unit) ((= ?y ?x)))",
+ t_implicitLeftJoin,
+ (String[])null);
+ }
+
+ @Test public void implicitLeftJoin20()
+ {
+ // Covers the case of one variable on left and neither on right
+ test(
+ "(leftjoin (bgp (?x ?p ?o)) (table unit) ((= ?x ?y)))",
+ t_implicitLeftJoin,
+ (String[])null);
+
+ // Swapping the order of the equality expression should make no difference
+ test(
+ "(leftjoin (bgp (?x ?p ?o)) (table unit) ((= ?y ?x)))",
+ t_implicitLeftJoin,
+ (String[])null);
+ }
+
+ @Test public void implicitLeftJoin21()
+ {
+ // Covers the case of one variable on left and neither on right
+ test(
+ "(leftjoin (bgp (?y ?p ?o)) (table unit) ((= ?x ?y)))",
+ t_implicitLeftJoin,
+ (String[])null);
+
+ // Swapping the order of the equality expression should make no difference
+ test(
+ "(leftjoin (bgp (?y ?p ?o)) (table unit) ((= ?y ?x)))",
+ t_implicitLeftJoin,
+ (String[])null);
+ }
+
+ @Test public void implicitLeftJoin22()
+ {
+ // Covers the case of no variables actually being relevant
+ test(
+ "(leftjoin (bgp (?s ?p ?o)) (table unit) ((= ?x ?y)))",
+ t_implicitLeftJoin,
+ (String[])null);
+
+ // Swapping the order of the equality expression should make no difference
+ test(
+ "(leftjoin (bgp (?s ?p ?o)) (table unit) ((= ?y ?x)))",
+ t_implicitLeftJoin,
+ (String[])null);
+ }
+
+ @Test public void implicitLeftJoin23()
+ {
+ // Covers the case of neither variable on left and both on right
+ test(
+ "(leftjoin (table unit) (bgp (?x ?p ?o)(?x <http://pred> ?y)) ((= ?x ?y)))",
+ t_implicitLeftJoin,
+ "(leftjoin (table unit) (assign ((?x ?y)) (bgp (?y ?p ?o)(?y <http://pred> ?y))))");
+
+ // Swapping the order of the equality expression should make no difference
+ test(
+ "(leftjoin (table unit) (bgp (?x ?p ?o)(?x <http://pred> ?y)) ((= ?y ?x)))",
+ t_implicitLeftJoin,
+ "(leftjoin (table unit) (assign ((?y ?x)) (bgp (?x ?p ?o)(?x <http://pred> ?x))))");
+ }
+
@Test public void implicitLeftJoinConditional1()
{
// Can be optimized because not all assigns block linearization