/**
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 Feb 20, 2011
*/
package com.bigdata.bop.joinGraph;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import junit.framework.TestCase2;
import com.bigdata.bop.BOp;
import com.bigdata.bop.BOpBase;
import com.bigdata.bop.Constant;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IConstant;
import com.bigdata.bop.IConstraint;
import com.bigdata.bop.IPredicate;
import com.bigdata.bop.IValueExpression;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.ImmutableBOp;
import com.bigdata.bop.NV;
import com.bigdata.bop.Var;
import com.bigdata.bop.ap.Predicate;
import com.bigdata.bop.constraint.AND;
import com.bigdata.bop.constraint.BooleanValueExpression;
import com.bigdata.bop.constraint.Constraint;
import com.bigdata.bop.joinGraph.rto.JGraph;
/**
*
* This test suite is built around around BSBM Q5. Each test has an existing
* join path and a new vertex to be added to the join path. The question is
* whether or not the vertex <em>can join</em> with the join path using one or
* more shared variable(s). This tests a method used to incrementally grow a
* join path when it is dynamically decided that an {@link IPredicate} may be
* added to the join path based on shared variables. Static analysis easily
* reports those joins which are allowed based on the variables directly given
* with two {@link IPredicate}s. The purpose of this test suite is to explore
* when joins (based on shared variables) become permissible through
* {@link IConstraint}s as the variable(s) used within those constraints become
* bound.
* <p>
* Note: To avoid a dependency on the RDF model layer, this just uses String
* constants for URIs and Literals.
* <h2>Analysis of BSBM Q5</h2>
* The following predicates all join on {@link #product}:
* <ul>
* <li>{@link #p0}</li>
* <li>{@link #p2}</li>
* <li>{@link #p4}</li>
* <li>{@link #p5}</li>
* </ul>
* The predicates ({@link #p3} and {@link #p5}) do not directly join with any of
* the other predicates (they do not directly share any variables). In general,
* a join without shared variables means the cross product of the sources will
* be materialized and such joins should be run last.
* <p>
* However, in this case there are two SPARQL FILTERs ({@link #c1} and
* {@link #c2}) which (a) use those variables ({@link #origProperty1} and
* {@link #origProperty2}); and (b) can constrain the query. This means that
* running the predicates without shared variables and applying the constraints
* before the tail of the plan can in fact lead to a more efficient join path.
* <p>
* This set of unit tests explores various join paths and verifies that the
* canJoin() and canJoinUsingConstraints() methods correctly recognize edges by
* which a join path can be extended corresponding to both static and dynamic
* analysis of the query.
*
* @see PartitionedJoinGroup#canJoin(IPredicate, IPredicate)
* @see PartitionedJoinGroup#canJoinUsingConstraints(IPredicate[], IPredicate,
* IConstraint[])
* @see JGraph
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id: TestBOpUtility_canJoinUsingConstraints.java 4211 2011-02-20
* 21:20:44Z thompsonbry $
*
* @todo These are the full plans generated by the runtime and static
* optimizers. One way to test canJoinXXX() is to run out these join plans
* and verify that they report "true" in each case. However, the critical
* bit to test are join plans where the predicates w/o the shared
* variables can be run earlier due to the FILTERs.
*
* <pre>
* test_bsbm_q5 : static [0] : : ids=[1, 2, 4, 6, 0, 3, 5]
* test_bsbm_q5 : runtime[0] : : ids=[1, 2, 0, 4, 6, 3, 5]
* </pre>
*/
//@SuppressWarnings("unchecked")
public class TestPartitionedJoinGroup_canJoinUsingConstraints extends TestCase2 {
/**
*
*/
public TestPartitionedJoinGroup_canJoinUsingConstraints() {
}
/**
* @param name
*/
public TestPartitionedJoinGroup_canJoinUsingConstraints(String name) {
super(name);
}
/**
* Unit tests to verify that arguments are validated.
*
* @see PartitionedJoinGroup#canJoinUsingConstraints(IPredicate[], IPredicate,
* IConstraint[])
*/
public void test_canJoinUsingConstraints_illegalArgument() {
final IVariable<?> x = Var.var("x");
final IVariable<?> y = Var.var("y");
final IPredicate<?> p1 = new Predicate(new BOp[]{x});
final IPredicate<?> p2 = new Predicate(new BOp[]{y});
// path must not be null.
try {
PartitionedJoinGroup.canJoinUsingConstraints(//
null, // path
p1,// vertex
new IConstraint[0]// constraints
);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Expecting: " + IllegalArgumentException.class);
}
// vertex must not be null.
try {
PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[]{p1}, // path
null,// vertex
new IConstraint[0]// constraints
);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Expecting: " + IllegalArgumentException.class);
}
// path may not be empty.
try {
PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] {}, // path
p1,// vertex
new IConstraint[0]// constraints
);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Expecting: " + IllegalArgumentException.class);
}
// path elements may not be null.
try {
PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p2, null }, // path
p1,// vertex
new IConstraint[0]// constraints
);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Expecting: " + IllegalArgumentException.class);
}
// vertex must not appear in the path.
try {
PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p2, p1 }, // path
p1,// vertex
new IConstraint[0]// constraints
);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Expecting: " + IllegalArgumentException.class);
}
// constraint array may not contain null elements.
try {
PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p2 }, // path
p1,// vertex
new IConstraint[] { //
Constraint.wrap(new NEConstant(x, new Constant<Integer>(12))), //
null //
}// constraints
);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Expecting: " + IllegalArgumentException.class);
}
}
// The comparison operators.
static private final int GT = 0, LT = 1;// , EQ = 2, GTE = 3, LTE = 4;
// The math operators.
static private final int PLUS = 0, MINUS = 1;
// Annotation for the comparison or math operator.
static private final String OP = "op";
/**
* A do-nothing constraint. The constraint is never evaluated. It is only
* used to test the logic which decides when two predicates can join based
* on variable(s) shared via a constraint.
*/
static private final class MyCompareOp extends BOpBase
implements BooleanValueExpression {
private static final long serialVersionUID = 1L;
/**
* Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}.
*
* @param op
*/
public MyCompareOp(MyCompareOp op) {
super(op);
}
/**
* @param args
* @param annotations
*/
public MyCompareOp(BOp[] args, Map<String, Object> annotations) {
super(args, annotations);
}
public Boolean get(IBindingSet bindingSet) {
throw new UnsupportedOperationException();
}
}
/**
* A do-nothing constraint. The constraint is never evaluated. It is only
* used to test the logic which decides when two predicates can join based
* on variable(s) shared via a constraint.
*/
static private final class NEConstant extends BOpBase
implements BooleanValueExpression {
private static final long serialVersionUID = 1L;
/**
* Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}.
*
* @param op
*/
public NEConstant(NEConstant op) {
super(op);
}
/**
* @param args
* @param annotations
*/
public NEConstant(BOp[] args, Map<String, Object> annotations) {
super(args, annotations);
}
public NEConstant(IVariable<?> var, IConstant<?> value) {
this(new BOp[] { var, value }, null/* annotations */);
}
public Boolean get(IBindingSet bindingSet) {
throw new UnsupportedOperationException();
}
}
/**
* A do-nothing value expression. The expression is never evaluated. It is
* only used to test the logic which decides when two predicates can join
* based on variable(s) shared via a constraint.
*/
static private final class MathBOp extends ImmutableBOp implements
IValueExpression {
private static final long serialVersionUID = 1L;
/**
* Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}.
*
* @param op
*/
public MathBOp(final MathBOp op) {
super(op);
}
/**
* Required shallow copy constructor.
*
* @param args
* The operands.
* @param op
* The operation.
*/
public MathBOp(final BOp[] args, Map<String, Object> anns) {
super(args, anns);
if (args.length != 2 || args[0] == null || args[1] == null
|| getProperty(OP) == null) {
throw new IllegalArgumentException();
}
}
/**
*
* @param left
* The left operand.
* @param right
* The right operand.
* @param op
* The annotation specifying the operation to be performed on
* those operands.
*/
public MathBOp(final IValueExpression left,
final IValueExpression right, final int op) {
this(new BOp[] { left, right }, NV.asMap(new NV(OP, op)));
}
public Object get(IBindingSet bindingSet) {
throw new UnsupportedOperationException();
}
}
static private final String rdfs = "http://www.w3.org/2000/01/rdf-schema#";
static private final String bsbm = "http://www4.wiwiss.fu-berlin.de/bizer/bsbm/v01/vocabulary/";
static private final String rdfsLabel = rdfs + "label";
static private final String productFeature = bsbm + "productFeature";
static private final String productPropertyNumeric1 = "productPropertyNumeric1";
static private final String productPropertyNumeric2 = bsbm
+ "productPropertyNumeric2";
static private final String productInstance = "http://www4.wiwiss.fu-berlin.de/bizer/bsbm/v01/instances/dataFromProducer1/Product22";
private int nextId = 0;
final IVariable<?> product = Var.var("product");
final IVariable<?> productLabel = Var.var("productLabel");
final IVariable<?> prodFeature = Var.var("prodFeature");
final IVariable<?> simProperty1 = Var.var("simProperty1");
final IVariable<?> simProperty2 = Var.var("simProperty2");
final IVariable<?> origProperty1 = Var.var("origProperty1");
final IVariable<?> origProperty2 = Var.var("origProperty2");
/** ?product rdfs:label ?productLabel . */
final private IPredicate<?> p0 = new Predicate(new BOp[] {//
product, new Constant(rdfsLabel), productLabel },//
new NV(BOp.Annotations.BOP_ID, nextId++)//
);
/** productInstance bsbm:productFeature ?prodFeature . */
final private IPredicate<?> p1 = new Predicate(new BOp[] { //
new Constant(productInstance), new Constant(productFeature),
prodFeature },//
new NV(BOp.Annotations.BOP_ID, nextId++)//
);
/** ?product bsbm:productFeature ?prodFeature . */
final private IPredicate<?> p2 = new Predicate(new BOp[] { //
product, new Constant(productFeature), prodFeature },//
new NV(BOp.Annotations.BOP_ID, nextId++)//
);
/** productInstance bsbm:productPropertyNumeric1 ?origProperty1 . */
final private IPredicate<?> p3 = new Predicate(new BOp[] { //
new Constant<String>(productInstance),
new Constant(productPropertyNumeric1), origProperty1 },//
new NV(BOp.Annotations.BOP_ID, nextId++)//
);
/** ?product bsbm:productPropertyNumeric1 ?simProperty1 . */
final private IPredicate<?> p4 = new Predicate(new BOp[] { //
product, new Constant(productPropertyNumeric1), simProperty1 },//
new NV(BOp.Annotations.BOP_ID, nextId++)//
);
/** productInstance bsbm:productPropertyNumeric2 ?origProperty2 . */
final private IPredicate<?> p5 = new Predicate(new BOp[] { //
new Constant(productInstance),
new Constant(productPropertyNumeric2), origProperty2 },//
new NV(BOp.Annotations.BOP_ID, nextId++)//
);
/** ?product bsbm:productPropertyNumeric2 ?simProperty2 . */
final private IPredicate<?> p6 = new Predicate(new BOp[] { //
product, new Constant(productPropertyNumeric2), simProperty2 },//
new NV(BOp.Annotations.BOP_ID, nextId++)//
);
/** The vertices of the join graph (the predicates). */
final IPredicate<?>[] preds = new IPredicate[] { p0, p1, p2, p3, p4, p5, p6 };
/**
* FILTER (productInstance != ?product)
*/
final IConstraint c0 = Constraint.wrap(new NEConstant(product, new Constant<String>(
productInstance)));
/**
* FILTER (?simProperty1 < (?origProperty1 + 120) && ?simProperty1 >
* (?origProperty1 - 120))
* <p>
* Note: The AND in the compound filters is typically optimized out such
* that each of these is represented as its own IConstraint, but I have
* combined them for the purposes of these unit tests.
*/
final IConstraint c1 = Constraint.wrap(new AND(//
new MyCompareOp(
new BOp[] {
simProperty1,
new MathBOp(origProperty1, new Constant<Integer>(
120), PLUS) }, NV.asMap(new NV[] { new NV(
OP, LT) })), //
new MyCompareOp(new BOp[] {
simProperty1,
new MathBOp(origProperty1, new Constant<Integer>(120),
MINUS) }, NV.asMap(new NV[] { new NV(OP, GT) }))//
));
/**
* FILTER (?simProperty2 < (?origProperty2 + 170) && ?simProperty2 >
* (?origProperty2 - 170))
* <p>
* Note: The AND in the compound filters is typically optimized out such
* that each of these is represented as its own IConstraint, but I have
* combined them for the purposes of these unit tests.
*/
final IConstraint c2 = Constraint.wrap(new AND(//
new MyCompareOp(
new BOp[] {
simProperty2,
new MathBOp(origProperty2, new Constant<Integer>(
170), PLUS) }, NV.asMap(new NV[] { new NV(
OP, LT) })),//
new MyCompareOp(new BOp[] {
simProperty2,
new MathBOp(origProperty2, new Constant<Integer>(170),
MINUS) }, NV.asMap(new NV[] { new NV(OP, GT) }))//
));
/** The constraints on the join graph. */
final IConstraint[] constraints = new IConstraint[] { c0, c1, c2 };
/**
* Unit test for one-step joins based on the {@link #product} variable.
*/
public void test_canJoinUsingConstraints_1step_productVar() {
// share ?product
final IPredicate<?>[] a = new IPredicate[] { p0, p2, p4, p6 };
for (int i = 0; i < a.length; i++) {
for (int j = i; j < a.length; j++) {
final IPredicate<?> t0 = a[i];
final IPredicate<?> t1 = a[j];
assertTrue(PartitionedJoinGroup.canJoin(t0, t1));
assertTrue(PartitionedJoinGroup.canJoin(t1, t0));
if (t0 != t1) {
/*
* Test join path extension, but not when the vertex used to
* extend the path is already present in the join path.
*/
assertTrue(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { t0 }, // path
t1,// vertex
new IConstraint[0]// constraints
));
assertTrue(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { t1 }, // path
t0,// vertex
new IConstraint[0]// constraints
));
}
}
}
}
/**
* Unit test for multi-step join paths based on the {@link #product}
* variable.
*/
public void test_canJoinUsingConstraints_multiStep_productVar() {
final Random r = new Random();
// share ?product
final IPredicate<?>[] a = new IPredicate[] { p0, p2, p4, p6 };
// existing path length [1:3].
final int existingPathLength = r.nextInt(3)+1;
// generated pre-existing path.
final IPredicate<?>[] path = new IPredicate[existingPathLength];
// vertex which will extend that path
final IPredicate<?> vertex;
{
// collection of predicates used so far by the path.
final Set<Integer> used = new LinkedHashSet<Integer>();
for (int i = 0; i < path.length; i++) {
// Locate an unused predicate.
int index;
while (true) {
index = r.nextInt(a.length);
if (!used.contains(index)) {
used.add(index);
break;
}
}
// add to the path.
path[i] = a[index];
}
// Locate an unused predicate to serve as the extension vertex.
{
// Locate an unused predicate.
int index;
while (true) {
index = r.nextInt(a.length);
if (!used.contains(index)) {
used.add(index);
break;
}
}
vertex = a[index];
}
}
// Verify all joins in the path are legal.
for (int i = 0; i < path.length - 1; i++) {
assertTrue(PartitionedJoinGroup.canJoin(path[i], path[i + 1]));
}
// Verify the extension of the path is legal.
assertTrue(PartitionedJoinGroup.canJoinUsingConstraints(//
path,//
vertex,//
new IConstraint[0]// constraints
));
}
/**
* Unit test examines the predicates without shared variables and verifies
* (a) that joins are not permitted when the constraints are not considered;
* and (b) that joins are permitted when the constraints are considered.
* <p>
* This test is identical to {@link #test_canJoinUsingConstraints_p5_p6()()}
* except that it considers the ({@link #p3} x {@link #p4}) join via the
* {@link #c1} constraint instead.
*/
public void test_canJoinUsingConstraints_p3_p4() {
/*
* Verify (p3,p4) join is not permitted when we do not consider the
* constraints (i.e., the join would be an unconstrained cross product
* if it were executed).
*/
assertFalse(PartitionedJoinGroup.canJoin(p3, p4));
assertFalse(PartitionedJoinGroup.canJoin(p4, p3));
assertFalse(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p3 }, // path
p4,// vertex
new IConstraint[0]// constraints
));
assertFalse(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p4 }, // path
p3,// vertex
new IConstraint[0]// constraints
));
/*
* Verify (p3,p4) join is not permitted if we do not consider the
* constraint which provides the shared variables.
*/
assertFalse(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p3 }, // path
p4,// vertex
new IConstraint[] { c2 }// constraints
));
assertFalse(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p4 }, // path
p3,// vertex
new IConstraint[] { c2 }// constraints
));
/*
* Verify (p3,p4) join is permitted if we consider the constraint which
* provides the shared variables.
*/
assertTrue(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p3 }, // path
p4,// vertex
new IConstraint[] { c1 }// constraints
));
assertTrue(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p4 }, // path
p3,// vertex
new IConstraint[] { c1 }// constraints
));
}
/**
* Unit test examines the predicates without shared variables and verifies
* (a) that joins are not permitted when the constraints are not considered;
* and (b) that joins are permitted when the constraints are considered.
* <p>
* This test is identical to {@link #test_canJoinUsingConstraints_p3_p4()}
* except that it considers the ({@link #p5} x {@link #p6}) join via the
* {@link #c2} constraint instead.
*/
public void test_canJoinUsingConstraints_p5_p6() {
/*
* Verify (p5,p6) join is not permitted when we do not consider the
* constraints (i.e., the join would be an unconstrained cross product
* if it were executed).
*/
assertFalse(PartitionedJoinGroup.canJoin(p5, p6));
assertFalse(PartitionedJoinGroup.canJoin(p6, p5));
assertFalse(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p5 }, // path
p6,// vertex
new IConstraint[0]// constraints
));
assertFalse(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p6 }, // path
p5,// vertex
new IConstraint[0]// constraints
));
/*
* Verify (p5,p6) join is not permitted if we do not consider the
* constraint which provides the shared variables.
*/
assertFalse(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p5 }, // path
p6,// vertex
new IConstraint[] { c1 }// constraints
));
assertFalse(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p6 }, // path
p5,// vertex
new IConstraint[] { c1 }// constraints
));
/*
* Verify (p5,p6) join is permitted if we consider the constraint which
* provides the shared variables.
*/
assertTrue(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p5 }, // path
p6,// vertex
new IConstraint[] { c2 }// constraints
));
assertTrue(PartitionedJoinGroup.canJoinUsingConstraints(//
new IPredicate[] { p6 }, // path
p5,// vertex
new IConstraint[] { c2 }// constraints
));
}
/*
* Unit tests for attaching constraints using a specific join path.
*/
private final Set<IConstraint> asSet(IConstraint[] a) {
return new LinkedHashSet<IConstraint>(Arrays.asList(a));
}
/** no constraints. */
final Set<IConstraint> NA = Collections.emptySet();
/** {@link #c0} attaches when any of [p0,p2,p4,p6] runs for the 1st time. */
final Set<IConstraint> C0 = asSet(new IConstraint[]{c0});
/** {@link #c1} attaches when 2nd of (p3,p4) runs (in any order). */
final Set<IConstraint> C1 = asSet(new IConstraint[] { c1 });
/** {@link #c2} attaches when 2nd of (p5,p6) runs (in any order). */
final Set<IConstraint> C2 = asSet(new IConstraint[] { c2 });
/** <code>path = [1, 2, 4, 6, 0, 3, 5]</code> */
public void test_attachConstraints_BSBM_Q5_path01() {
final IPredicate<?>[] path = { p1, p2, p4, p6, p0, p3, p5 };
final IConstraint[][] actual = PartitionedJoinGroup
.getJoinGraphConstraints(path, constraints,
null/* knownBoundVars */, true/* pathIsComplete */);
final Set<IConstraint>[] expected = new Set[] { //
NA, // p1
C0, // p2
NA, // p4
NA, // p6
NA, // p0
C1, // p3
C2, // p5
};
assertSameConstraints(expected, actual);
}
/** <code>[5, 3, 1, 0, 2, 4, 6]</code>. */
public void test_attachConstraints_BSBM_Q5_path02() {
final IPredicate<?>[] path = { p5, p3, p1, p0, p2, p4, p6 };
final IConstraint[][] actual = PartitionedJoinGroup
.getJoinGraphConstraints(path, constraints,
null/* knownBoundVars */, true/* pathIsComplete */);
final Set<IConstraint>[] expected = new Set[] { //
NA, // p5
NA, // p3
NA, // p1
C0, // p0
NA, // p2
C1, // p4
C2, // p6
};
assertSameConstraints(expected, actual);
}
/** <code>[3, 4, 5, 6, 1, 2, 0]</code> (key-range constraint variant). */
public void test_attachConstraints_BSBM_Q5_path03() {
final IPredicate<?>[] path = { p3, p4, p5, p6, p1, p2, p0 };
final IConstraint[][] actual = PartitionedJoinGroup
.getJoinGraphConstraints(path, constraints,
null/* knownBoundVars */, true/* pathIsComplete */);
final Set<IConstraint>[] expected = new Set[] { //
NA, // p3
asSet(new IConstraint[]{c0,c1}), // p4
NA, // p5
C2, // p6
NA, // p1
NA, // p2
NA, // p0
};
assertSameConstraints(expected, actual);
}
/**
* <code>[5 6 0 2 1 4 3]</code>.
*
* FIXME The above join path produces a false ZERO result for the query and
* all of the join path segments below produce a false exact ZERO (0E)
* cardinality estimate. Figure out why. The final path chosen could have
* been any of the one step extensions of a path with a false 0E cardinality
* estimate.
*
* <pre>
* INFO : 3529 main com.bigdata.bop.joinGraph.rto.JGraph.expand(JGraph.java:1116):
* ** round=4: paths{in=14,considered=26,out=6}
* path srcCard * f ( in sumRgCt tplsRead out limit adjCard) = estRead estCard : sumEstRead sumEstCard sumEstCost joinPath
* 0 0E * 0.00 ( 0 0 0 0 200 0) = 0 0E : 1 0 0 [ 5 6 0 2 1 4 ]
* 1 0E * 0.00 ( 0 0 0 0 200 0) = 0 0E : 1 0 0 [ 5 6 0 2 4 3 ]
* 2 0E * 0.00 ( 0 0 0 0 200 0) = 0 0E : 1 0 0 [ 5 6 0 4 1 3 ]
* 3 0E * 0.00 ( 0 0 0 0 200 0) = 0 0E : 1 0 0 [ 5 6 2 1 4 3 ]
* 4 208 * 1.00 ( 26 26 26 26 400 26) = 26 208 : 16576 1447 1447 [ 5 3 1 2 4 0 ]
* 5 0E * 0.00 ( 0 0 0 0 200 0) = 0 0E : 2 1 1 [ 5 3 6 0 1 2 ]
* </pre>
*/
public void test_attachConstraints_BSBM_Q5_path04() {
final IPredicate<?>[] path = { p5, p6, p0, p2, p1, p4, p3 };
final IConstraint[][] actual = PartitionedJoinGroup
.getJoinGraphConstraints(path, constraints,
null/* knownBoundVars */, true/* pathIsComplete */);
final Set<IConstraint>[] expected = new Set[] { //
NA, // p5
asSet(new IConstraint[] { c0, c2 }), // p6
NA, // p0
NA, // p2
NA, // p1
NA, // p4
C1, // p3
};
assertSameConstraints(expected, actual);
}
/**
* Verifies that the right set of constraints is attached at each of the
* vertices of a join path. Comparison of {@link IConstraint} instances is
* by reference.
*
* @param expected
* @param actual
*/
static void assertSameConstraints(final Set<IConstraint>[] expected,
final IConstraint[][] actual) {
assertEquals("length", expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
final Set<IConstraint> e = expected[i];
final IConstraint[] a = actual[i];
if (e.size() != a.length) {
fail("Differs at expected[" + i + "] : expecting " + e.size()
+ ", not " + a.length + " elements: "
+ Arrays.toString(a));
}
for (int j = 0; j < a.length; j++) {
boolean foundRef = false;
for (IConstraint t : e) {
if (t == a[j]) {
foundRef = true;
break;
}
}
if (!foundRef) {
fail("Differs at expected[" + i + "][" + j + "] : actual="
+ a[j]);
}
}
}
}
}