/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Oct 17, 2011 */ package com.bigdata.bop.join; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.FutureTask; import junit.framework.TestCase; import org.openrdf.model.Value; import org.openrdf.model.impl.LiteralImpl; import org.openrdf.model.impl.URIImpl; import org.openrdf.model.vocabulary.RDF; import com.bigdata.bop.BOp; import com.bigdata.bop.BOpContext; import com.bigdata.bop.Constant; import com.bigdata.bop.IBindingSet; import com.bigdata.bop.IConstant; import com.bigdata.bop.IConstraint; import com.bigdata.bop.IVariable; import com.bigdata.bop.NV; import com.bigdata.bop.PipelineOp; import com.bigdata.bop.Var; import com.bigdata.bop.bindingSet.ListBindingSet; import com.bigdata.bop.constraint.Constraint; import com.bigdata.bop.constraint.EQConstant; import com.bigdata.bop.engine.AbstractQueryEngineTestCase; import com.bigdata.bop.engine.BOpStats; import com.bigdata.rdf.internal.IV; import com.bigdata.rdf.internal.VTE; import com.bigdata.rdf.internal.XSD; import com.bigdata.rdf.internal.impl.TermId; import com.bigdata.rdf.model.BigdataValue; import com.bigdata.rdf.model.BigdataValueFactory; import com.bigdata.rdf.model.BigdataValueFactoryImpl; import com.bigdata.rdf.vocab.decls.FOAFVocabularyDecl; import com.bigdata.relation.accesspath.IBuffer; import com.bigdata.striterator.Chunkerator; import cutthecrap.utils.striterators.ICloseableIterator; /** * Test suite for both {@link HTreeHashJoinUtility} and {@link JVMHashJoinUtility}. * * TODO Unit test of optional solutions * * TODO Verify application of constraints, but only to the non-optional * solutions (so this means a test with optionals and also we need a test w/o * constraints). * * TODO See {@link TestHTreeHashJoinOp} and some of the other join test suites for * some of these variations. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ abstract public class AbstractHashJoinUtilityTestCase extends TestCase { public AbstractHashJoinUtilityTestCase() { } public AbstractHashJoinUtilityTestCase(String name) { super(name); } /** * Setup for a problem used by many of the join test suites. */ static public class JoinSetup { public final String namespace; public final IV<?, ?> brad, john, fred, mary, paul, leon; public JoinSetup(final String namespace) { if (namespace == null) throw new IllegalArgumentException(); this.namespace = namespace; brad = makeIV(new LiteralImpl("Brad")); john = makeIV(new LiteralImpl("John")); fred = makeIV(new LiteralImpl("Fred")); mary = makeIV(new LiteralImpl("Mary")); paul = makeIV(new LiteralImpl("Paul")); leon = makeIV(new LiteralImpl("Leon")); } /** * Return a (Mock) IV for a Value. * * @param v * The value. * * @return The Mock IV. */ @SuppressWarnings({ "unchecked", "rawtypes" }) private IV makeIV(final Value v) { final BigdataValueFactory valueFactory = BigdataValueFactoryImpl .getInstance(namespace); final BigdataValue bv = valueFactory.asValue(v); final IV iv = new TermId(VTE.valueOf(v), nextId++); iv.setValue(bv); return iv; } private long nextId = 1L; // Note: First id MUST NOT be 0L !!! /** * <pre> * {x=brad, y=fred} * {x=mary} * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getLeft1() { final IVariable<?> x = Var.var("x"); final IVariable<?> y = Var.var("y"); // The left solutions (the pipeline). final List<IBindingSet> left = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(x, new Constant<IV>(brad)); tmp.set(y, new Constant<IV>(fred)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(x, new Constant<IV>(mary)); left.add(tmp); return left; } /** * <pre> * {a=paul, x=mary} * {a=paul, x=brad} * {a=john, x=mary} * {a=john, x=brad} * {a=mary, x=brad} * {a=brad, x=fred} * {a=brad, x=leon} * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getRight1() { final IVariable<?> a = Var.var("a"); final IVariable<?> x = Var.var("x"); // The right solutions (the hash index). final List<IBindingSet> right = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(a, new Constant<IV>(paul)); tmp.set(x, new Constant<IV>(mary)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(a, new Constant<IV>(paul)); tmp.set(x, new Constant<IV>(brad)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(a, new Constant<IV>(john)); tmp.set(x, new Constant<IV>(mary)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(a, new Constant<IV>(john)); tmp.set(x, new Constant<IV>(brad)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(a, new Constant<IV>(mary)); tmp.set(x, new Constant<IV>(brad)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(a, new Constant<IV>(brad)); tmp.set(x, new Constant<IV>(fred)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(a, new Constant<IV>(brad)); tmp.set(x, new Constant<IV>(leon)); right.add(tmp); return right; } } /** * <pre> * @prefix : <http://example/> . * @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . * @prefix foaf: <http://xmlns.com/foaf/0.1/> . * * :alice rdf:type foaf:Person . * :alice foaf:name "Alice" . * :bob rdf:type foaf:Person . * </pre> */ static public class ExistsSetup { public final String namespace; public final IV<?, ?> alice, bob, rdfType, foafName, foafPerson, aliceLabel; public ExistsSetup(final String namespace) { if (namespace == null) throw new IllegalArgumentException(); this.namespace = namespace; alice = makeIV(new URIImpl("http://example/alice")); bob = makeIV(new URIImpl("http://example/bob")); rdfType = makeIV(RDF.TYPE); foafName = makeIV(FOAFVocabularyDecl.name); foafPerson = makeIV(FOAFVocabularyDecl.Person); aliceLabel = makeIV(new LiteralImpl("Alice")); } /** * Return a (Mock) IV for a Value. * * @param v * The value. * * @return The Mock IV. */ @SuppressWarnings({ "unchecked", "rawtypes" }) private IV makeIV(final Value v) { final BigdataValueFactory valueFactory = BigdataValueFactoryImpl .getInstance(namespace); final BigdataValue bv = valueFactory.asValue(v); final IV iv = new TermId(VTE.valueOf(v), nextId++); iv.setValue(bv); return iv; } private long nextId = 1L; // Note: First id MUST NOT be 0L !!! /** * Reading from access path * * <pre> * ?person rdf:type foaf:Person * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getLeft1() { final IVariable<?> person = Var.var("person"); // The left solutions (the pipeline). final List<IBindingSet> left = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(person, new Constant<IV>(alice)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(person, new Constant<IV>(bob)); left.add(tmp); return left; } /** * Reading from access path * * <pre> * ?person foaf:name ?name * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getRight1() { final IVariable<?> person = Var.var("person"); final IVariable<?> name = Var.var("name"); // The right solutions (the hash index). final List<IBindingSet> right = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(person, new Constant<IV>(alice)); tmp.set(name, new Constant<IV>(aliceLabel)); right.add(tmp); return right; } } /** * Setup for NOT EXISTS problem. * * <pre> * @prefix : <http://example/> . * * :a :p 1 ; :q 1, 2 . * :b :p 3.0 ; :q 4.0, 5.0 . * </pre> */ static public class NotExistsSetup { public final String namespace; public final IV<?, ?> a, b, one, two, three, four, five; public NotExistsSetup(final String namespace) { if (namespace == null) throw new IllegalArgumentException(); this.namespace = namespace; a = makeIV(new LiteralImpl("a")); b = makeIV(new LiteralImpl("b")); one = makeIV(new LiteralImpl("1", XSD.INTEGER)); two = makeIV(new LiteralImpl("2", XSD.INTEGER)); three = makeIV(new LiteralImpl("3.0", XSD.DECIMAL)); four = makeIV(new LiteralImpl("4.0", XSD.DECIMAL)); five = makeIV(new LiteralImpl("5.0", XSD.DECIMAL)); } /** * Return a (Mock) IV for a Value. * * @param v * The value. * * @return The Mock IV. */ @SuppressWarnings({ "unchecked", "rawtypes" }) private IV makeIV(final Value v) { final BigdataValueFactory valueFactory = BigdataValueFactoryImpl .getInstance(namespace); final BigdataValue bv = valueFactory.asValue(v); final IV iv = new TermId(VTE.valueOf(v), nextId++); iv.setValue(bv); return iv; } private long nextId = 1L; // Note: First id MUST NOT be 0L !!! /** * Reading from access path having this data and binding <code>?a</code> * and <code>?n</code> * * <pre> * :a :p 1 . * :b :p 3.0 . * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getLeft1() { final IVariable<?> avar = Var.var("a"); final IVariable<?> nvar = Var.var("n"); // The left solutions (the pipeline). final List<IBindingSet> left = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(avar, new Constant<IV>(a)); tmp.set(nvar, new Constant<IV>(one)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(avar, new Constant<IV>(b)); tmp.set(nvar, new Constant<IV>(three)); left.add(tmp); return left; } /** * Reading from access path having this data and binding <code>?a</code> * and <code>?m</code> * * <pre> * :a :q 1 . * :a :q 2 . * :b :q 4.0 . * :b :q 5.0 . * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getRight1(final IVariable<?> ovar) { final IVariable<?> avar = Var.var("a"); // final IVariable<?> mvar = Var.var("m"); // The right solutions (the hash index). final List<IBindingSet> right = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(avar, new Constant<IV>(a)); tmp.set(ovar, new Constant<IV>(one)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(avar, new Constant<IV>(a)); tmp.set(ovar, new Constant<IV>(two)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(avar, new Constant<IV>(b)); tmp.set(ovar, new Constant<IV>(four)); right.add(tmp); tmp = new ListBindingSet(); tmp.set(avar, new Constant<IV>(b)); tmp.set(ovar, new Constant<IV>(five)); right.add(tmp); return right; } } @SuppressWarnings("deprecation") protected static void assertSameSolutionsAnyOrder( final IBindingSet[] expected, final Iterator<IBindingSet> actual) { AbstractQueryEngineTestCase.assertSameSolutionsAnyOrder(expected, actual); } /** * A buffer which absorbs solutions and let's us replay them via an * iterator. */ protected static class TestBuffer<E> implements IBuffer<E> { @SuppressWarnings({ "rawtypes", "unchecked" }) private final List<E> a = new LinkedList(); public Iterator<E> iterator() { return a.iterator(); } public int size() { return a.size(); } public boolean isEmpty() { return a.isEmpty(); } public void add(E e) { a.add(e); } public long flush() { return 0; } public void reset() { } } protected class MockPipelineOp extends PipelineOp { public MockPipelineOp(final BOp[] args, final NV... anns) { super(args, NV.asMap(anns)); }; private static final long serialVersionUID = 1L; @Override public FutureTask<Void> eval(BOpContext<IBindingSet> context) { throw new UnsupportedOperationException(); } } /** * Test helper for required or optional join tests. * * @param joinType * @param joinVars * @param selectVars * @param left * @param right * @param expected */ protected void doHashJoinTest(// final JoinTypeEnum joinType,// final IVariable<?>[] joinVars,// final IVariable<?>[] selectVars,// final IConstraint[] constraints,// final List<IBindingSet> left, // final List<IBindingSet> right,// final IBindingSet[] expected// ) { // Setup a mock PipelineOp for the test. final PipelineOp op = new MockPipelineOp(BOp.NOARGS, new NV(HTreeHashJoinAnnotations.RELATION_NAME, new String[] { getName() }),// new NV(HashJoinAnnotations.JOIN_VARS, joinVars),// new NV(JoinAnnotations.SELECT, selectVars),// new NV(JoinAnnotations.CONSTRAINTS, constraints)// ); final IHashJoinUtility state = newHashJoinUtility(op, joinType); try { // Load the right solutions into the HTree. { final BOpStats stats = new BOpStats(); state.acceptSolutions( new Chunkerator<IBindingSet>(right.iterator()), stats); assertEquals(right.size(), state.getRightSolutionCount()); assertEquals(right.size(), stats.unitsIn.get()); // Verify all expected solutions are in the hash index. assertSameSolutionsAnyOrder( right.toArray(new IBindingSet[right.size()]), state.indexScan()); } /* * Run the hash join. */ { // final ICloseableIterator<IBindingSet> leftItr = new CloseableIteratorWrapper<IBindingSet>( // left.iterator()); final ICloseableIterator<IBindingSet[]> leftItr = new Chunkerator<IBindingSet>( left.iterator(), 100/*chunkSize*/, IBindingSet.class); // Buffer used to collect the solutions. final TestBuffer<IBindingSet> outputBuffer = new TestBuffer<IBindingSet>(); // Compute the "required" solutions. state.hashJoin(leftItr, null/* stats */, outputBuffer); switch (joinType) { case Normal: break; case Optional: case NotExists: // Output the optional solutions. state.outputOptionals(outputBuffer); break; case Exists: // Output the join set. state.outputJoinSet(outputBuffer); break; default: throw new AssertionError(); } // Verify the expected solutions. assertSameSolutionsAnyOrder(expected, outputBuffer.iterator()); } } finally { state.release(); } } /** * Factory for {@link IHashJoinUtility} instances under test. * * @param op * The operator from which much of the state will be initialized. * @param joinType * The type of operation to be performed. * @return */ abstract protected IHashJoinUtility newHashJoinUtility(PipelineOp op, final JoinTypeEnum joinType); /** * Empty lhs and rhs with non-optional join. */ public void test_hashJoin01() { // the join variables. final IVariable<?>[] joinVars = new IVariable[]{}; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null; // the join constraints. final IConstraint[] constraints = null; // The left solutions (the pipeline). final List<IBindingSet> left = new LinkedList<IBindingSet>(); // The right solutions (the hash index). final List<IBindingSet> right = new LinkedList<IBindingSet>(); // The expected solutions to the join. final IBindingSet[] expected = new IBindingSet[0]; doHashJoinTest(JoinTypeEnum.Normal, joinVars, selectVars, constraints, left, right, expected); } /** * Empty lhs and rhs with optional join. */ public void test_hashJoin02() { // the join variables. final IVariable<?>[] joinVars = new IVariable[]{}; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null; // the join constraints. final IConstraint[] constraints = null; // The left solutions (the pipeline). final List<IBindingSet> left = new LinkedList<IBindingSet>(); // The right solutions (the hash index). final List<IBindingSet> right = new LinkedList<IBindingSet>(); // The expected solutions to the join. final IBindingSet[] expected = new IBindingSet[0]; doHashJoinTest(JoinTypeEnum.Optional, joinVars, selectVars, constraints, left, right, expected); } /** * Non-optional join. */ @SuppressWarnings("rawtypes") public void test_hashJoin03() { final JoinSetup setup = new JoinSetup(getName()); final IVariable<?> a = Var.var("a"); final IVariable<?> x = Var.var("x"); final IVariable<?> y = Var.var("y"); // the join variables. final IVariable<?>[] joinVars = new IVariable[]{x}; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = new IVariable[]{x,y}; // the join constraints. final IConstraint[] constraints = new IConstraint[] { Constraint .wrap(new EQConstant(a, new Constant<IV>(setup.john))) }; // The left solutions (the pipeline). final List<IBindingSet> left = setup.getLeft1(); // The right solutions (the hash index). final List<IBindingSet> right = setup.getRight1(); // The expected solutions to the join. final IBindingSet[] expected = new IBindingSet[] {// new ListBindingSet(// new IVariable[] { x },// new IConstant[] { new Constant<IV>(setup.mary) }// ),// new ListBindingSet(// new IVariable[] { x, y },// new IConstant[] { new Constant<IV>(setup.brad), new Constant<IV>(setup.fred), }// ),// }; doHashJoinTest(JoinTypeEnum.Normal, joinVars, selectVars, constraints, left, right, expected); } /** * Variant of a previous non-optional join with no join variables. */ @SuppressWarnings("rawtypes") public void test_hashJoin04() { final JoinSetup setup = new JoinSetup(getName()); final IVariable<?> a = Var.var("a"); final IVariable<?> x = Var.var("x"); final IVariable<?> y = Var.var("y"); // the join variables. final IVariable<?>[] joinVars = new IVariable[]{}; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = new IVariable[]{x,y}; // the join constraints. final IConstraint[] constraints = new IConstraint[] { Constraint .wrap(new EQConstant(a, new Constant<IV>(setup.john))) }; // The left solutions (the pipeline). final List<IBindingSet> left = setup.getLeft1(); // The right solutions (the hash index). final List<IBindingSet> right = setup.getRight1(); // The expected solutions to the join. final IBindingSet[] expected = new IBindingSet[] {// new ListBindingSet(// new IVariable[] { x },// new IConstant[] { new Constant<IV>(setup.mary) }// ),// new ListBindingSet(// new IVariable[] { x, y },// new IConstant[] { new Constant<IV>(setup.brad), new Constant<IV>(setup.fred), }// ),// }; doHashJoinTest(JoinTypeEnum.Normal, joinVars, selectVars, constraints, left, right, expected); } /** * Variant of the previous non-optional join without select variables. */ @SuppressWarnings("rawtypes") public void test_hashJoin05() { final JoinSetup setup = new JoinSetup(getName()); final IVariable<?> a = Var.var("a"); final IVariable<?> x = Var.var("x"); final IVariable<?> y = Var.var("y"); // the join variables. final IVariable<?>[] joinVars = new IVariable[]{}; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null; // the join constraints. final IConstraint[] constraints = new IConstraint[] { Constraint .wrap(new EQConstant(a, new Constant<IV>(setup.john))) }; // The left solutions (the pipeline). final List<IBindingSet> left = setup.getLeft1(); // The right solutions (the hash index). final List<IBindingSet> right = setup.getRight1(); // The expected solutions to the join. final IBindingSet[] expected = new IBindingSet[] {// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary) }// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.brad), new Constant<IV>(setup.fred), }// ),// }; doHashJoinTest(JoinTypeEnum.Normal, joinVars, selectVars, constraints, left, right, expected); } /** * Unit test of a required MERGE JOIN involving two sources. The join * variable is <code>?a</code>. One source has <code>(?a,?x)</code>. The * other has <code>(?a,?y)</code>. For this test, all solutions having the * same value for the join variable will join (there are no join constraints * and the solutions can not conflict as they do not bind the same variables * for the non-join variables). * <p> * The following represents the solutions for each source organized by the * join variable <code>?a</code>. * <pre> * (?a, ?x) (?a, ?y) * (john, mary) (john, brad) // many-to-many join * (john, leon) (john, fred) * (john, leon) * (mary, john) (mary, brad) // 1-1 join. * (fred, brad) // does not join (would join if OPTIONAL). * (brad, fred) // does not join. * (leon, john) (leon, brad) // many-1 join. * (leon, mary) // * (paul, leon) (paul, leon) // 1-many join. * (paul, brad) * </pre> */ public void test_mergeJoin01() { final JoinSetup setup = new JoinSetup(getName()); final boolean optional = false; final IVariable<?> a = Var.var("a"); final IVariable<?> x = Var.var("x"); final IVariable<?> y = Var.var("y"); // The join variables. This must be the same for each source. final IVariable<?>[] joinVars = new IVariable[]{a}; // The variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null; // The join constraints. final IConstraint[] constraints = null; /** * Setup the solutions for [first]. */ @SuppressWarnings("rawtypes") final IBindingSet[] firstSolutions = new IBindingSet[] { new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.mary), new Constant<IV>(setup.john) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.fred), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.john) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.mary) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.leon) }// ),// }; assertEquals(7,firstSolutions.length); /** * Setup the source solutions for [other]. */ @SuppressWarnings("rawtypes") final IBindingSet[] otherSolutions = new IBindingSet[] { new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.fred) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.mary), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.brad), new Constant<IV>(setup.fred) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.leon) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.brad) }// ),// }; assertEquals(8,otherSolutions.length); /** * The expected solutions to the join. * * The source solutions are: * <pre> * (?a, ?x) (?a, ?y) * (john, mary) (john, brad) // many-to-many join * (john, leon) (john, fred) * (john, leon) * (mary, john) (mary, brad) // 1-1 join. * (fred, brad) // does not join. * (brad, fred) // does not join. * (leon, john) (leon, brad) // many-1 join. * (leon, mary) // * (paul, leon) (paul, leon) // 1-many join. * (paul, brad) * </pre> */ @SuppressWarnings("rawtypes") final IBindingSet[] expected = new IBindingSet[] {// // many-to-many join new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.brad)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.fred)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.leon)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.brad)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.fred)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.leon)}// ),// // 1-to-1 join new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.mary), new Constant<IV>(setup.john), new Constant<IV>(setup.brad)}// ),// // many-to-1 join new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.john), new Constant<IV>(setup.brad)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.mary), new Constant<IV>(setup.brad)}// ),// // 1-many join new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.leon), new Constant<IV>(setup.leon)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.leon), new Constant<IV>(setup.brad)}// ),// }; IHashJoinUtility first = null; IHashJoinUtility other = null; try { // Setup a mock PipelineOp for the test. final PipelineOp firstOp = new MockPipelineOp(BOp.NOARGS, new NV(HTreeHashJoinAnnotations.RELATION_NAME, new String[] { getName() }),// new NV(HashJoinAnnotations.JOIN_VARS, joinVars),// new NV(JoinAnnotations.SELECT, selectVars)// // new NV(JoinAnnotations.CONSTRAINTS, constraints)// ); // Setup a mock PipelineOp for the test. final PipelineOp otherOp = new MockPipelineOp(BOp.NOARGS, new NV(HTreeHashJoinAnnotations.RELATION_NAME, new String[] { getName() }),// new NV(HashJoinAnnotations.JOIN_VARS, joinVars),// new NV(JoinAnnotations.SELECT, selectVars)// // new NV(JoinAnnotations.CONSTRAINTS, constraints)// ); first = newHashJoinUtility(firstOp, optional ? JoinTypeEnum.Optional : JoinTypeEnum.Normal); other = newHashJoinUtility(otherOp, optional ? JoinTypeEnum.Optional : JoinTypeEnum.Normal); // Load into [first]. { final BOpStats stats = new BOpStats(); first.acceptSolutions(new Chunkerator<IBindingSet>(Arrays .asList(firstSolutions).iterator()), stats); assertEquals(firstSolutions.length, first.getRightSolutionCount()); assertEquals(firstSolutions.length, stats.unitsIn.get()); } // Load into [other]. { final BOpStats stats = new BOpStats(); other.acceptSolutions(new Chunkerator<IBindingSet>(Arrays .asList(otherSolutions).iterator()), stats); assertEquals(otherSolutions.length, other.getRightSolutionCount()); assertEquals(otherSolutions.length, stats.unitsIn.get()); } // Do the merge join and verify the expected solutions. doMergeJoinTest(constraints, expected, optional, first, other); } finally { if (first != null) { first.release(); } if (other != null) { other.release(); } } } /** * Test for optional merge join. This is based on the same data as the * non-optional merge join test above. */ public void test_mergeJoin02() { final JoinSetup setup = new JoinSetup(getName()); final boolean optional = true; final IVariable<?> a = Var.var("a"); final IVariable<?> x = Var.var("x"); final IVariable<?> y = Var.var("y"); // The join variables. This must be the same for each source. final IVariable<?>[] joinVars = new IVariable[]{a}; // The variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null; // The join constraints. final IConstraint[] constraints = null; /** * Setup the solutions for [first]. */ @SuppressWarnings("rawtypes") final IBindingSet[] firstSolutions = new IBindingSet[] { new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.mary), new Constant<IV>(setup.john) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.fred), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.john) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.mary) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.leon) }// ),// }; assertEquals(7,firstSolutions.length); /** * Setup the source solutions for [other]. */ @SuppressWarnings("rawtypes") final IBindingSet[] otherSolutions = new IBindingSet[] { new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.fred) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.mary), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.brad), new Constant<IV>(setup.fred) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.leon) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.brad) }// ),// }; assertEquals(8,otherSolutions.length); /** * The expected solutions to the join. * * The source solutions are: * <pre> * (?a, ?x) (?a, ?y) * (john, mary) (john, brad) // many-to-many join * (john, leon) (john, fred) * (john, leon) * (mary, john) (mary, brad) // 1-1 join. * (fred, brad) // OPTIONAL JOIN. * (brad, fred) // does not join. * (leon, john) (leon, brad) // many-1 join. * (leon, mary) // * (paul, leon) (paul, leon) // 1-many join. * (paul, brad) * </pre> */ @SuppressWarnings("rawtypes") final IBindingSet[] expected = new IBindingSet[] {// // many-to-many join new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.brad)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.fred)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.leon)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.brad)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.fred)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.leon)}// ),// // 1-to-1 join new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.mary), new Constant<IV>(setup.john), new Constant<IV>(setup.brad)}// ),// // OPTIONAL JOIN new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.fred), new Constant<IV>(setup.brad)}// ),// // many-to-1 join new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.john), new Constant<IV>(setup.brad)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.leon), new Constant<IV>(setup.mary), new Constant<IV>(setup.brad)}// ),// // 1-many join new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.leon), new Constant<IV>(setup.leon)}// ),// new ListBindingSet(// new IVariable[] { a, x, y },// new IConstant[] { new Constant<IV>(setup.paul), new Constant<IV>(setup.leon), new Constant<IV>(setup.brad)}// ),// }; IHashJoinUtility first = null; IHashJoinUtility other = null; try { // Setup a mock PipelineOp for the test. final PipelineOp firstOp = new MockPipelineOp(BOp.NOARGS, new NV(HTreeHashJoinAnnotations.RELATION_NAME, new String[] { getName() }),// new NV(HashJoinAnnotations.JOIN_VARS, joinVars),// new NV(JoinAnnotations.SELECT, selectVars)// // new NV(JoinAnnotations.CONSTRAINTS, constraints)// ); // Setup a mock PipelineOp for the test. final PipelineOp otherOp = new MockPipelineOp(BOp.NOARGS, new NV(HTreeHashJoinAnnotations.RELATION_NAME, new String[] { getName() }),// new NV(HashJoinAnnotations.JOIN_VARS, joinVars),// new NV(JoinAnnotations.SELECT, selectVars)// // new NV(JoinAnnotations.CONSTRAINTS, constraints)// ); first = newHashJoinUtility(firstOp, optional ? JoinTypeEnum.Optional : JoinTypeEnum.Normal); other = newHashJoinUtility(otherOp, optional ? JoinTypeEnum.Optional : JoinTypeEnum.Normal); // Load into [first]. { final BOpStats stats = new BOpStats(); first.acceptSolutions(new Chunkerator<IBindingSet>(Arrays .asList(firstSolutions).iterator()), stats); assertEquals(firstSolutions.length, first.getRightSolutionCount()); assertEquals(firstSolutions.length, stats.unitsIn.get()); } // Load into [other]. { final BOpStats stats = new BOpStats(); other.acceptSolutions(new Chunkerator<IBindingSet>(Arrays .asList(otherSolutions).iterator()), stats); assertEquals(otherSolutions.length, other.getRightSolutionCount()); assertEquals(otherSolutions.length, stats.unitsIn.get()); } // Do the merge join and verify the expected solutions. doMergeJoinTest(constraints, expected, optional, first, other); } finally { if (first != null) { first.release(); } if (other != null) { other.release(); } } } /** * Test with more than 2 join sets. */ public void test_mergeJoin03_nonOpt() { mergeJoin03(false, false); } public void test_mergeJoin03_opt() { mergeJoin03(true, false); } public void test_mergeJoin03_nonOptConstrain() { mergeJoin03(false, true); } public void test_mergeJoin03_optConstrain() { mergeJoin03(true, true); } /** * * <pre> * (?a ?x) (?a ?y) (?a ?z) * (john mary) (john brad) (john mary) * (john leon) (john fred) * (fred leon) * </pre> * * <pre> * (?a ?x ?y ?z) * (john mary brad mary) // required join * (john mary fred mary) // required join unless constraint * (john leon brad mary) // required join * (john leon fred mary) // required join unless constraint * (fred leon ---- ----) // iff optional (regardless of constraint) * </pre> * * @param optional * @param constrain * When <code>true</code>, we constrain <code>?y = brad</code> */ @SuppressWarnings("rawtypes") public void mergeJoin03(final boolean optional, final boolean constrain) { final JoinSetup setup = new JoinSetup(getName()); final IVariable<?> a = Var.var("a"); final IVariable<?> x = Var.var("x"); final IVariable<?> y = Var.var("y"); final IVariable<?> z = Var.var("z"); // The join variables. This must be the same for each source. final IVariable<?>[] joinVars = new IVariable[]{a}; // The variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null; // The join constraints. final IConstraint[] constraints = constrain ? new IConstraint[] { Constraint .wrap(new EQConstant(y, new Constant<IV>(setup.brad))),// } : null; /** * Setup the solutions for [first]. */ final IBindingSet[] firstSolutions = new IBindingSet[] { new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon) }// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.fred), new Constant<IV>(setup.leon) }// )// }; assertEquals(3,firstSolutions.length); /** * Setup the source solutions for [other]. */ final IBindingSet[] otherSolutions = new IBindingSet[] { new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.brad) }// ),// new ListBindingSet(// new IVariable[] { a, y },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.fred) }// ) }; assertEquals(2,otherSolutions.length); /** * Setup the source solutions for [other]. */ final IBindingSet[] moreSolutions = new IBindingSet[] { new ListBindingSet(// new IVariable[] { a, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary) }// ),// }; assertEquals(1,moreSolutions.length); /** * The expected solutions to the join. * * The source solutions are: * <pre> * (?a, ?x) (?a, ?y) (?a, ?z) * (john, mary) (john, brad) (john, mary)// many-to-many join * (john, leon) (john, fred) * (fred, lyon) * </pre> */ final IBindingSet[] expected; if (optional) { if (constraints == null) { expected = new IBindingSet[] {// // many-to-many join new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.brad), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.fred), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.brad), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.fred), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.fred), new Constant<IV>(setup.leon)}) }; } else { expected = new IBindingSet[] {// // many-to-many join new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.brad), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.brad), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x },// new IConstant[] { new Constant<IV>(setup.fred), new Constant<IV>(setup.leon)}) }; } } else { if (constraints == null) { expected = new IBindingSet[] {// // many-to-many join new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.brad), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.fred), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.brad), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.fred), new Constant<IV>(setup.mary)}// ) }; } else { expected = new IBindingSet[] {// // many-to-many join new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.mary), new Constant<IV>(setup.brad), new Constant<IV>(setup.mary)}// ),// new ListBindingSet(// new IVariable[] { a, x, y, z },// new IConstant[] { new Constant<IV>(setup.john), new Constant<IV>(setup.leon), new Constant<IV>(setup.brad), new Constant<IV>(setup.mary)}// ) }; } } IHashJoinUtility first = null; IHashJoinUtility other = null; IHashJoinUtility more = null; try { // Setup a mock PipelineOp for the test. final PipelineOp firstOp = new MockPipelineOp(BOp.NOARGS, new NV(HTreeHashJoinAnnotations.RELATION_NAME, new String[] { getName() }),// new NV(HashJoinAnnotations.JOIN_VARS, joinVars),// new NV(JoinAnnotations.SELECT, selectVars),// new NV(JoinAnnotations.CONSTRAINTS, constraints)// ); // Setup a mock PipelineOp for the test. final PipelineOp otherOp = new MockPipelineOp(BOp.NOARGS, new NV(HTreeHashJoinAnnotations.RELATION_NAME, new String[] { getName() }),// new NV(HashJoinAnnotations.JOIN_VARS, joinVars),// new NV(JoinAnnotations.SELECT, selectVars),// new NV(JoinAnnotations.CONSTRAINTS, constraints)// ); // Setup a mock PipelineOp for the test. final PipelineOp moreOp = new MockPipelineOp(BOp.NOARGS, new NV(HTreeHashJoinAnnotations.RELATION_NAME, new String[] { getName() }),// new NV(HashJoinAnnotations.JOIN_VARS, joinVars),// new NV(JoinAnnotations.SELECT, selectVars),// new NV(JoinAnnotations.CONSTRAINTS, constraints)// ); first = newHashJoinUtility(firstOp, optional ? JoinTypeEnum.Optional : JoinTypeEnum.Normal); other = newHashJoinUtility(otherOp, optional ? JoinTypeEnum.Optional : JoinTypeEnum.Normal); more = newHashJoinUtility(moreOp, optional ? JoinTypeEnum.Optional : JoinTypeEnum.Normal); // Load into [first]. { final BOpStats stats = new BOpStats(); first.acceptSolutions(new Chunkerator<IBindingSet>(Arrays .asList(firstSolutions).iterator()), stats); assertEquals(firstSolutions.length, first.getRightSolutionCount()); assertEquals(firstSolutions.length, stats.unitsIn.get()); } // Load into [other]. { final BOpStats stats = new BOpStats(); other.acceptSolutions(new Chunkerator<IBindingSet>(Arrays .asList(otherSolutions).iterator()), stats); assertEquals(otherSolutions.length, other.getRightSolutionCount()); assertEquals(otherSolutions.length, stats.unitsIn.get()); } // Load into [more]. { final BOpStats stats = new BOpStats(); more.acceptSolutions(new Chunkerator<IBindingSet>(Arrays .asList(moreSolutions).iterator()), stats); assertEquals(moreSolutions.length, more.getRightSolutionCount()); assertEquals(moreSolutions.length, stats.unitsIn.get()); } // Do the merge join and verify the expected solutions. doMergeJoinTest(constraints, expected, optional, first, other, more); } finally { if (first != null) { first.release(); } if (other != null) { other.release(); } if (more != null) { more.release(); } } } /** * @param first * The instances from which the required joins will be reported * if the join is OPTIONAL. * @param others * The other instances. There must be at least two instances to * be joined when [first] and [others] are considered together. * @param constraints * The join constraints. * @param expected * The expected solutions. */ protected void doMergeJoinTest( final IConstraint[] constraints,// final IBindingSet[] expected,// final boolean optional,// final IHashJoinUtility first,// final IHashJoinUtility... others ) { // Buffer used to collect the solutions. final TestBuffer<IBindingSet> outputBuffer = new TestBuffer<IBindingSet>(); // Do the merge join, first.mergeJoin(others, outputBuffer, constraints, optional); // Verify the expected solutions. assertEquals(expected.length, outputBuffer.size()); assertSameSolutionsAnyOrder(expected, outputBuffer.iterator()); } /** * Unit test for EXISTS based on Sesame <code>sparql1-exists-01</code>. * * <pre> * PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> * PREFIX foaf: <http://xmlns.com/foaf/0.1/> * * SELECT ?person * WHERE * { * ?person rdf:type foaf:Person . * FILTER EXISTS { ?person foaf:name ?name } * } * </pre> * * <pre> * @prefix : <http://example/> . * @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . * @prefix foaf: <http://xmlns.com/foaf/0.1/> . * * :alice rdf:type foaf:Person . * :alice foaf:name "Alice" . * :bob rdf:type foaf:Person . * </pre> * * <pre> * <result> * <binding name="person"> * <uri>http://example/alice</uri> * </binding> * </result> * </pre> */ public void test_exists_01() { final ExistsSetup setup = new ExistsSetup(getName()); final IVariable<?> person = Var.var("person"); // the join variables. final IVariable<?>[] joinVars = new IVariable[]{person}; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null;//new IVariable[] { person }; // the join constraints. final IConstraint[] constraints = null; // The left solutions (the pipeline). final List<IBindingSet> right = setup.getLeft1(); // The right solutions (the hash index). final List<IBindingSet> left = setup.getRight1(); // The expected solutions to the join. @SuppressWarnings("rawtypes") final IBindingSet[] expected = new IBindingSet[] {// new ListBindingSet(// new IVariable[] { person},// new IConstant[] { new Constant<IV>(setup.alice) }// ),// }; doHashJoinTest(JoinTypeEnum.Exists, joinVars, selectVars, constraints, left, right, expected); } /** * Unit tests for NOT EXISTS based on Sesame <code>sparql11-exists-05</code> * . * * <pre> * PREFIX : <http://example/> * SELECT * WHERE { * ?a :p ?n * FILTER NOT EXISTS { * ?a :q ?n . * } * } * * :a :p 1 . * :b :p 3.0 . * * :a :q 1 . * :a :q 2 . * :b :q 4.0 . * :b :q 5.0 . * * <result> * <binding name="a"> * <uri>http://example/b</uri> * </binding> * <binding name="n"> * <literal datatype="http://www.w3.org/2001/XMLSchema#decimal">3.0</literal> * </binding> * </result> * * </pre> */ public void test_not_exists_01() { final NotExistsSetup setup = new NotExistsSetup(getName()); final IVariable<?> avar = Var.var("a"); final IVariable<?> nvar = Var.var("n"); // the join variables. final IVariable<?>[] joinVars = new IVariable[]{avar}; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null;//new IVariable[] { avar, nvar }; // the join constraints. final IConstraint[] constraints = null; // The left solutions (the pipeline). final List<IBindingSet> right = setup.getLeft1(); // The right solutions (the hash index). final List<IBindingSet> left = setup.getRight1(nvar); // The expected solutions to the join. @SuppressWarnings("rawtypes") final IBindingSet[] expected = new IBindingSet[] {// new ListBindingSet(// new IVariable[] { avar, nvar },// new IConstant[] { new Constant<IV>(setup.b), new Constant<IV>(setup.three) }// ),// }; doHashJoinTest(JoinTypeEnum.NotExists, joinVars, selectVars, constraints, left, right, expected); } /* * Note: This test has been removed. The inner FILTER needs to be applied to * the join which reads from the inner access path. Thus, if we were to * model it in this test suite, the filter is effectively inoperative since * [?n] is not bound in that scope. The test is actually attaching the * FILTER to the MINUS join, which is NOT how the SPARQL query was written. * So, this test reduces to exactly the same thing as the one above unless * we are testing filter placement and evaluation rather than testing the * MINUS join operator. */ // /** // * Unit tests for NOT EXISTS based on Sesame <code>sparql11-exists-06</code> // * . This uses the same data as the previous test (and has the same // * solutions), but the query is slightly different and includes a FILTER // * inside of the EXISTS graph pattern. // * <p> // * NOTE: Due to the scope of the variables, <code>?n</code> IS NOT BOUND // * inside of the FILTER! // * // * <pre> // * PREFIX : <http://example/> // * SELECT * WHERE { // * ?a :p ?n // * FILTER NOT EXISTS { // * ?a :q ?m . // * FILTER(?n = ?m) // * } // * } // * // * <result> // * <binding name="a"> // * <uri>http://example/b</uri> // * </binding> // * <binding name="n"> // * <literal datatype="http://www.w3.org/2001/XMLSchema#decimal">3.0</literal> // * </binding> // * </result> // * // * </pre> // * // * @see http://www.w3.org/TR/sparql11-query/#negation, section on inner // * filters. // */ // public void test_not_exists_02() { // // final NotExistsSetup setup = new NotExistsSetup(getName()); // // final IVariable<?> avar = Var.var("a"); // final IVariable<?> nvar = Var.var("n"); // final IVariable<?> mvar = Var.var("m"); // // // the join variables. // final IVariable<?>[] joinVars = new IVariable[]{avar}; // // // the variables projected by the join (iff non-null). // final IVariable<?>[] selectVars = null;//new IVariable[] { avar, nvar }; // // // the join constraints. // final IConstraint[] constraints = new IConstraint[] { Constraint // .wrap(new EQ(nvar, mvar)) }; // // // The left solutions (the pipeline). // final List<IBindingSet> right = setup.getLeft1(); // // // The right solutions (the hash index). // final List<IBindingSet> left = setup.getRight1(mvar); // // // The expected solutions to the join. // @SuppressWarnings("rawtypes") // final IBindingSet[] expected = new IBindingSet[] {// // new ListBindingSet(// // new IVariable[] { avar, nvar },// // new IConstant[] { new Constant<IV>(setup.b), // new Constant<IV>(setup.three) // }// // ),// // }; // // doHashJoinTest(JoinTypeEnum.NotExists, joinVars, selectVars, // constraints, left, right, expected); // // } /** * Setup a problem based on the following query, which is * <code>service02</code> from the openrdf SPARQL 1.1 Federated Query test * suite. * * <pre> * SELECT ?s ?o1 ?o2 * { * SERVICE <http://localhost:18080/openrdf/repositories/endpoint1> { * ?s ?p ?o1 . } * OPTIONAL { * SERVICE <http://localhost:18080/openrdf/repositories/endpoint2> { * ?s ?p2 ?o2 } * } * } * </pre> * * Solutions for endpoint1: * * <pre> * { s=http://example.org/a, p=http://xmlns.com/foaf/0.1/name, o1="Alan" } * { s=http://example.org/b, p=http://xmlns.com/foaf/0.1/name, o1="Bob" } * </pre> * * Solutions for endpoint2: * * <pre> * { s=http://example.org/a, p2=http://xmlns.com/foaf/0.1/interest, o2="SPARQL 1.1 Basic Federated Query" } * </pre> */ static public class JoinSetup_service02 { public final String namespace; public final IV<?, ?> a, b, foafName, alan, bob, foafInterest, label; public JoinSetup_service02(final String namespace) { if (namespace == null) throw new IllegalArgumentException(); this.namespace = namespace; a = makeIV(new URIImpl("http://example.org/a")); b = makeIV(new URIImpl("http://example.org/b")); foafName = makeIV(new URIImpl("http://xmlns.com/foaf/0.1/name")); alan = makeIV(new LiteralImpl("Alan")); bob = makeIV(new LiteralImpl("Bob")); foafInterest = makeIV(new URIImpl("http://xmlns.com/foaf/0.1/interest")); label = makeIV(new LiteralImpl("SPARQL 1.1 Basic Federated Query")); } /** * Return a (Mock) IV for a Value. * * @param v * The value. * * @return The Mock IV. */ @SuppressWarnings({ "unchecked", "rawtypes" }) private IV makeIV(final Value v) { final BigdataValueFactory valueFactory = BigdataValueFactoryImpl .getInstance(namespace); final BigdataValue bv = valueFactory.asValue(v); final IV iv = new TermId(VTE.valueOf(v), nextId++); iv.setValue(bv); return iv; } private long nextId = 1L; // Note: First id MUST NOT be 0L !!! /** * Solutions for end point 1. * * <pre> * { s=http://example.org/a, p=http://xmlns.com/foaf/0.1/name, o1="Alan" } * { s=http://example.org/b, p=http://xmlns.com/foaf/0.1/name, o1="Bob" } * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getLeft1() { final IVariable<?> s = Var.var("s"); final IVariable<?> p = Var.var("p"); final IVariable<?> o1 = Var.var("o1"); // The left solutions (the pipeline). final List<IBindingSet> left = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(a)); tmp.set(p, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(alan)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(b)); tmp.set(p, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(bob)); left.add(tmp); return left; } /** * Solutions for end point 2. * * <pre> * { s=http://example.org/a, p2=http://xmlns.com/foaf/0.1/interest, o2="SPARQL 1.1 Basic Federated Query" } * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getRight1() { final IVariable<?> s = Var.var("s"); final IVariable<?> p2 = Var.var("p2"); final IVariable<?> o2 = Var.var("o2"); // The right solutions (the hash index). final List<IBindingSet> right = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(a)); tmp.set(p2, new Constant<IV>(foafInterest)); tmp.set(o2, new Constant<IV>(label)); right.add(tmp); return right; } /** * Solutions for the ServiceCallJoin with end point 2. It combines the * first left solution with the first (and only) right solution. * * <pre> * <result> * <binding name="s"><uri>http://example.org/a</uri></binding> * p=foaf:name * <binding name="o1"><literal>Alan</literal></binding> * p2=foaf:interest * <binding name="o2"><literal>SPARQL 1.1 Basic Federated Query</literal></binding> * </result> * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getServiceCall2JoinSolutions() { final IVariable<?> s = Var.var("s"); final IVariable<?> p = Var.var("p"); final IVariable<?> o1 = Var.var("o1"); final IVariable<?> p2 = Var.var("p2"); final IVariable<?> o2 = Var.var("o2"); final List<IBindingSet> right = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(a)); tmp.set(p, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(alan)); tmp.set(p2, new Constant<IV>(foafInterest)); tmp.set(o2, new Constant<IV>(label)); right.add(tmp); return right; } } /** * Unit test for the 2nd SERVICE hash join for {@link JoinSetup_service02}. */ public void test_service02() { final JoinSetup_service02 setup = new JoinSetup_service02(getName()); final IVariable<?> s = Var.var("s"); // final IVariable<?> p = Var.var("p"); // final IVariable<?> o1 = Var.var("o1"); // final IVariable<?> p2 = Var.var("p2"); // final IVariable<?> o2 = Var.var("o2"); // the join variables. final IVariable<?>[] joinVars = new IVariable[] { s }; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null;//new IVariable[] { s, o1, o2 }; // the join constraints. final IConstraint[] constraints = null; // The left solutions (the pipeline). final List<IBindingSet> right = setup.getLeft1(); // The right solutions (the hash index). final List<IBindingSet> left = setup.getRight1(); // The expected solutions to the join. final IBindingSet[] expected = setup.getServiceCall2JoinSolutions() .toArray(new IBindingSet[] {}); doHashJoinTest(JoinTypeEnum.Normal, joinVars, selectVars, constraints, left, right, expected); } /** * Unit test for the OPTIONAL GROUP hash join for {@link JoinSetup_service02}. This * is the optional JOIN performed between the solutions from the 1st SERVICE * and the solutions from the 2nd SERVICE. The solutions to this join are * the solutions to the query, except that some variables are dropped by the * projection. We model the projection here in order to compare directly to * the expected solutions for the query. */ public void test_service02b() { final JoinSetup_service02 setup = new JoinSetup_service02(getName()); final IVariable<?> s = Var.var("s"); // final IVariable<?> p = Var.var("p"); final IVariable<?> o1 = Var.var("o1"); // final IVariable<?> p2 = Var.var("p2"); final IVariable<?> o2 = Var.var("o2"); // the join variables. final IVariable<?>[] joinVars = new IVariable[] { s }; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = new IVariable[] { s, o1, o2 }; // the join constraints. final IConstraint[] constraints = null; // The left solutions (the pipeline). final List<IBindingSet> right = setup.getLeft1(); // The right solutions (the hash index). final List<IBindingSet> left = setup.getServiceCall2JoinSolutions(); /** * This is the OPTIONAL solution in the original query. It should not be * produced for the problem which we are setting up here since we are * modeling the ServiceCallJoin. * * <pre> * <result> * <binding name="s"><uri>http://example.org/b</uri></binding> * <binding name="o1"><literal>Bob</literal></binding> * </result> * </pre> * * This is the non-OPTIONAL ServiceCallJoin solution. It combines the * first left solution with the first (and only) right solution. * * <pre> * <result> * <binding name="s"><uri>http://example.org/a</uri></binding> * <binding name="o1"><literal>Alan</literal></binding> * <binding name="o2"><literal>SPARQL 1.1 Basic Federated Query</literal></binding> * </result> * </pre> */ // The expected solutions to the join. @SuppressWarnings("rawtypes") final IBindingSet[] expected = new IBindingSet[] {// new ListBindingSet(// new IVariable[] { s, o1 },// new IConstant[] { new Constant<IV>(setup.b), new Constant<IV>(setup.bob) }// ),// new ListBindingSet(// new IVariable[] { s, o1, o2},// new IConstant[] { new Constant<IV>(setup.a), new Constant<IV>(setup.alan), new Constant<IV>(setup.label), }// ),// }; doHashJoinTest(JoinTypeEnum.Optional, joinVars, selectVars, constraints, left, right, expected); } /** * Setup a problem based on the following query, which is * <code>service04</code> from the openrdf SPARQL 1.1 Federated Query test * suite. * * <pre> * PREFIX : <http://example.org/> * PREFIX foaf: <http://xmlns.com/foaf/0.1/> * SELECT ?s ?o1 ?o2 * { * ?s ?p1 ?o1 * OPTIONAL { SERVICE <http://localhost:18080/openrdf/repositories/endpoint1> {?s foaf:knows ?o2 }} * } BINDINGS ?o2 { * (:b) * } * </pre> * * Solutions for the local access path (?s ?p1 ?o1): * <pre> * :a foaf:name "Alan" . * :a foaf:mbox "alan@example.org" . * :b foaf:name "Bob" . * :b foaf:mbox "bob@example.org" . * :c foaf:name "Alice" . * :c foaf:mbox "alice@example.org" . * </pre> * * Solutions for endpoint1: * * <pre> * { o2=http://example.org/b, s=http://example.org/a } * { o2=http://example.org/b, s=http://example.org/a } * </pre> */ static public class JoinSetup_service04 { public final String namespace; public final IV<?, ?> a, b, c, foafName, foafMbox, alan, bob, alice, alanEmail, bobEmail, aliceEmail; public JoinSetup_service04(final String namespace) { if (namespace == null) throw new IllegalArgumentException(); this.namespace = namespace; a = makeIV(new URIImpl("http://example.org/a")); b = makeIV(new URIImpl("http://example.org/b")); c = makeIV(new URIImpl("http://example.org/c")); foafName = makeIV(new URIImpl("http://xmlns.com/foaf/0.1/name")); foafMbox = makeIV(new URIImpl("http://xmlns.com/foaf/0.1/mbox")); alan = makeIV(new LiteralImpl("Alan")); bob = makeIV(new LiteralImpl("Bob")); alice = makeIV(new LiteralImpl("Alice")); alanEmail = makeIV(new LiteralImpl("alan@example.org")); bobEmail = makeIV(new LiteralImpl("bob@example.org")); aliceEmail = makeIV(new LiteralImpl("alice@example.org")); } /** * Return a (Mock) IV for a Value. * * @param v * The value. * * @return The Mock IV. */ @SuppressWarnings({ "unchecked", "rawtypes" }) private IV makeIV(final Value v) { final BigdataValueFactory valueFactory = BigdataValueFactoryImpl .getInstance(namespace); final BigdataValue bv = valueFactory.asValue(v); final IV iv = new TermId(VTE.valueOf(v), nextId++); iv.setValue(bv); return iv; } private long nextId = 1L; // Note: First id MUST NOT be 0L !!! /** * Solutions for the local access path (?s ?p1 ?o1) * * <pre> * :a foaf:name "Alan" . * :a foaf:mbox "alan@example.org" . * { s=TermId(2U)[http://example.org/b], p1=TermId(4U)[http://xmlns.com/foaf/0.1/name], o1=TermId(7L)[Bob] }, * { s=TermId(2U)[http://example.org/b], p1=TermId(5U)[http://xmlns.com/foaf/0.1/mbox], o1=TermId(10L)[bob@example.org] }, * { s=TermId(3U)[http://example.org/c], p1=TermId(4U)[http://xmlns.com/foaf/0.1/name], o1=TermId(8L)[Alice] }, * { s=TermId(3U)[http://example.org/c], p1=TermId(5U)[http://xmlns.com/foaf/0.1/mbox], o1=TermId(11L)[alice@example.org] }] * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getSolutionsLocalAP() { final IVariable<?> s = Var.var("s"); final IVariable<?> p = Var.var("p1"); final IVariable<?> o1 = Var.var("o1"); // The left solutions (the pipeline). final List<IBindingSet> left = new LinkedList<IBindingSet>(); IBindingSet tmp; // alan tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(a)); tmp.set(p, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(alan)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(a)); tmp.set(p, new Constant<IV>(foafMbox)); tmp.set(o1, new Constant<IV>(alanEmail)); left.add(tmp); // bob tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(b)); tmp.set(p, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(bob)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(b)); tmp.set(p, new Constant<IV>(foafMbox)); tmp.set(o1, new Constant<IV>(bobEmail)); left.add(tmp); // alice tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(c)); tmp.set(p, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(alice)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(c)); tmp.set(p, new Constant<IV>(foafMbox)); tmp.set(o1, new Constant<IV>(aliceEmail)); left.add(tmp); return left; } /** * Solutions for end point 1. * * <pre> * { o2=http://example.org/b, s=http://example.org/a } * { o2=http://example.org/b, s=http://example.org/a } * </pre> */ @SuppressWarnings("rawtypes") List<IBindingSet> getSolutionsEndpoint1() { final IVariable<?> s = Var.var("s"); final IVariable<?> o2 = Var.var("o2"); // The left solutions (the pipeline). final List<IBindingSet> left = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(a)); tmp.set(o2, new Constant<IV>(b)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(a)); tmp.set(o2, new Constant<IV>(b)); left.add(tmp); return left; } /** * Solutions for the ServiceCallJoin with end point 2. It combines the * solutions from the local access path (left) with the solutions from * endpoint1 (right). * * <pre> * { o2=TermId(2U)[http://example.org/b], s=TermId(1U)[http://example.org/a], p1=TermId(5U), o1=TermId(8L) } * { o2=TermId(2U)[http://example.org/b], s=TermId(1U)[http://example.org/a], p1=TermId(6U), o1=TermId(7L) } * { o2=TermId(2U)[http://example.org/b], s=TermId(1U)[http://example.org/a], p1=TermId(5U), o1=TermId(8L) } * { o2=TermId(2U)[http://example.org/b], s=TermId(1U)[http://example.org/a], p1=TermId(6U), o1=TermId(7L) } * </pre> */ // 5=foaf:mbox // 6=foaf:name // 7=Alan // 9=Alice // 11=Bob // 8=alan@example.org // 10=alice@example.org // 12=bob@example.org @SuppressWarnings("rawtypes") List<IBindingSet> getServiceCallJoinSolutions() { final IVariable<?> o2 = Var.var("o2"); final IVariable<?> s = Var.var("s"); final IVariable<?> p1 = Var.var("p1"); final IVariable<?> o1 = Var.var("o1"); final List<IBindingSet> right = new LinkedList<IBindingSet>(); IBindingSet tmp; tmp = new ListBindingSet(); tmp.set(o2, new Constant<IV>(b)); tmp.set(s, new Constant<IV>(a)); tmp.set(p1, new Constant<IV>(foafMbox)); tmp.set(o1, new Constant<IV>(alanEmail)); right.add(tmp); right.add(tmp.clone()); // twice. tmp = new ListBindingSet(); tmp.set(o2, new Constant<IV>(b)); tmp.set(s, new Constant<IV>(a)); tmp.set(p1, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(alan)); right.add(tmp); right.add(tmp.clone()); // twice return right; } /** * The OPTIONAL group join will return the UNION the solutions from the * SERVICE which did join, which is * {@link #getServiceCallJoinSolutions()}, with the solutions from the * local access path which did not join. */ @SuppressWarnings("rawtypes") List<IBindingSet> getOptionalGroupSolutions() { final IVariable<?> s = Var.var("s"); final IVariable<?> p = Var.var("p1"); final IVariable<?> o1 = Var.var("o1"); final List<IBindingSet> left = new LinkedList<IBindingSet>(); left.addAll(getServiceCallJoinSolutions()); IBindingSet tmp; // bob tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(b)); tmp.set(p, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(bob)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(b)); tmp.set(p, new Constant<IV>(foafMbox)); tmp.set(o1, new Constant<IV>(bobEmail)); left.add(tmp); // alice tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(c)); tmp.set(p, new Constant<IV>(foafName)); tmp.set(o1, new Constant<IV>(alice)); left.add(tmp); tmp = new ListBindingSet(); tmp.set(s, new Constant<IV>(c)); tmp.set(p, new Constant<IV>(foafMbox)); tmp.set(o1, new Constant<IV>(aliceEmail)); left.add(tmp); return left; } } /** * Unit test for the SERVICE hash join for {@link JoinSetup_service04}. */ public void test_service04a() { final JoinSetup_service04 setup = new JoinSetup_service04(getName()); final IVariable<?> s = Var.var("s"); // the join variables. final IVariable<?>[] joinVars = new IVariable[] { s }; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null;//new IVariable[] { s, o1, o2 }; // the join constraints. final IConstraint[] constraints = null; // The left solutions (the pipeline). final List<IBindingSet> right = setup.getSolutionsLocalAP(); // The right solutions (the hash index). final List<IBindingSet> left = setup.getSolutionsEndpoint1(); // The expected solutions to the join. final IBindingSet[] expected = setup.getServiceCallJoinSolutions() .toArray(new IBindingSet[] {}); doHashJoinTest(JoinTypeEnum.Normal, joinVars, selectVars, constraints, left, right, expected); } /** * Unit test for the OPTIONAL hash join for {@link JoinSetup_service04}. The * solutions for the query are just the PROJECTION of (?s ?o1 ?o2) for this * join. */ public void test_service04b() { final JoinSetup_service04 setup = new JoinSetup_service04(getName()); final IVariable<?> s = Var.var("s"); // the join variables. final IVariable<?>[] joinVars = new IVariable[] { s }; // the variables projected by the join (iff non-null). final IVariable<?>[] selectVars = null;//new IVariable[] { s, o1, o2 }; // the join constraints. final IConstraint[] constraints = null; // The left solutions (the pipeline). final List<IBindingSet> right = setup.getSolutionsLocalAP(); // The right solutions (the hash index). final List<IBindingSet> left = setup.getServiceCallJoinSolutions(); // The expected solutions to the join. final IBindingSet[] expected = setup.getOptionalGroupSolutions() .toArray(new IBindingSet[] {}); doHashJoinTest(JoinTypeEnum.Optional, joinVars, selectVars, constraints, left, right, expected); } }