com.bigdata.bop.joinGraph.PartitionedJoinGroup Maven / Gradle / Ivy
package com.bigdata.bop.joinGraph;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import com.bigdata.bop.BOp;
import com.bigdata.bop.BOpUtility;
import com.bigdata.bop.Bind;
import com.bigdata.bop.IConstraint;
import com.bigdata.bop.IPredicate;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.joinGraph.rto.JoinGraph;
import com.bigdata.rdf.sparql.ast.StaticAnalysis_CanJoin;
/**
* Class accepts a join group and partitions it into a join graph and a tail
* plan.
*
* A join group consists of an ordered collection of {@link IPredicate}s and an
* unordered collection of {@link IConstraint}s. {@link IPredicate} representing
* non-optional joins are extracted into a {@link JoinGraph} along with any
* {@link IConstraint}s whose variables are guaranteed to be bound by the
* implied joins.
*
* The remainder of the {@link IPredicate}s and {@link IConstraint}s form a
* "tail plan". {@link IConstraint}s in the tail plan are attached to the last
* {@link IPredicate} at which their variable(s) MIGHT have become bound.
*
* @author Bryan Thompson
*
* @todo Things like LET can also bind variables. So can a subquery. Analysis of
* those will tell us whether the variable will definitely or
* conditionally become bound (I am assuming that a LET can conditionally
* leave a variable unbound). See {@link Bind}.
*
* @todo runFirst flag on the expander (for free text search). this should be an
* annotation. this can be a [headPlan]. [There can be constraints which
* are evaluated against the head plan. They need to get attached to the
* joins generated for the head plan. MikeP writes: There is a free text
* search access path that replaces the actual access path for the
* predicate, which is meaningless in an of itself because the P is
* magical.]
*
* @deprecated by {@link StaticAnalysis_CanJoin} which is a port of this code to
* the AST mode.
*/
public class PartitionedJoinGroup {
private static final transient Logger log = Logger
.getLogger(PartitionedJoinGroup.class);
/**
* The set of variables bound by the non-optional predicates.
*/
private final Set> joinGraphVars = new LinkedHashSet>();
/**
* The set of non-optional predicates which have been flagged as
* "run first". These are usually special access paths created using an
* expander which replaces a mock access path. For example, free text
* search.
*/
private final List> headPlan = new LinkedList>();
/**
* The set of non-optional predicates which represent the join graph.
*/
private final List> joinGraph = new LinkedList>();
/**
* The set of constraints which can be evaluated with the head plan and/or
* join graph predicates because the variables appearing in those
* constraints are known to become bound within the join graph. ( The
* {@link #headPlan} and the {@link #joinGraph} share the same
* pool of constraints.)
*/
private final List joinGraphConstraints = new LinkedList();
/**
* A set of optional predicates which will be run after the join graph.
*/
private final List> tailPlan = new LinkedList>();
/**
* An unordered list of those constraints containing at least one variable
* known be bound (and optionally) bound within the tail plan.
*/
private final List tailPlanConstraints = new LinkedList();
/**
* A map indicating which constraints are run for which predicate in the
* tail plan. The keys are the bopIds of the predicates in the tail plan.
* The values are the sets of constraints to run for that tail.
*
* @todo This assumes that the tail plan is not reordered.
*/
private final Map> tailPlanConstraintMap = new LinkedHashMap>();
/**
* The set of variables bound by the non-optional predicates (either the
* head plan or the join graph).
*/
public Set> getJoinGraphVars() {
return joinGraphVars;
}
/**
* The {@link IPredicate}s in the join graph (required joins).
*/
public IPredicate>[] getJoinGraph() {
return joinGraph.toArray(new IPredicate[joinGraph.size()]);
}
/**
* The {@link IConstraint}s to be applied to the {@link IPredicate}s in the
* join graph. Each {@link IConstraint} should be applied as soon as all of
* its variable(s) are known to be bound. The constraints are not attached
* to the {@link IPredicate}s in the join graph because the evaluation order
* of those {@link IPredicate}s is not yet known (it will be determined by a
* query optimizer when it decides on an evaluation order for those joins).
*/
public IConstraint[] getJoinGraphConstraints() {
final int size = joinGraphConstraints.size();
if (size == 0)
return IConstraint.EMPTY;
return joinGraphConstraints.toArray(new IConstraint[size]);
}
/**
* Return the set of constraints which should be attached to the last join
* in the given the join path. All joins in the join path must be
* non-optional joins (that is, part of either the head plan or the join
* graph).
*
* The rule followed by this method is that each constraint will be attached
* to the first non-optional join at which all of its variables are known to
* be bound. It is assumed that constraints are attached to each join in the
* join path by a consistent logic, e.g., as dictated by this method.
*
* @param joinPath
* An ordered array of predicate identifiers representing a
* specific sequence of non-optional joins.
* @param pathIsComplete
* true
iff the path represents a complete
* join path. When true
, any constraints which have
* not already been attached will be attached to the last
* predicate in the join path.
*
* @return The constraints which should be attached to the last join in the
* join path.
*
* @throws IllegalArgumentException
* if the join path is null
.
* @throws IllegalArgumentException
* if the join path is empty.
* @throws IllegalArgumentException
* if any element of the join path is null
.
* @throws IllegalArgumentException
* if any predicate specified in the join path is not known to
* this class.
* @throws IllegalArgumentException
* if any predicate specified in the join path is optional.
*
* @todo Implement (or refactor) the logic to decide which variables need to
* be propagated and which can be dropped. This decision logic will
* need to be available to the runtime query optimizer.
*
* @todo This does not pay attention to the head plan. If there can be
* constraints on the head plan then either this should be modified
* such that it can decide where they attach or we need to have a
* method which does the same thing for the head plan.
*/
public IConstraint[] getJoinGraphConstraints(final int[] pathIds,
final boolean pathIsComplete) {
/*
* Verify arguments and resolve bopIds to predicates.
*/
if (pathIds == null)
throw new IllegalArgumentException();
final IPredicate>[] path = new IPredicate[pathIds.length];
for (int i = 0; i < pathIds.length; i++) {
final int id = pathIds[i];
IPredicate> p = null;
for (IPredicate> tmp : joinGraph) {
if (tmp.getId() == id) {
p = tmp;
break;
}
}
if (p == null)
throw new IllegalArgumentException("Not found: id=" + id);
if (p.isOptional())
throw new AssertionError(
"Not expecting an optional predicate: " + p);
path[i] = p;
}
final IConstraint[] constraints = joinGraphConstraints
.toArray(new IConstraint[joinGraphConstraints.size()]);
final IConstraint[][] attachedConstraints = getJoinGraphConstraints(
path, constraints, null/* knownBound */, pathIsComplete);
return attachedConstraints[pathIds.length - 1];
}
// static public IConstraint[][] getJoinGraphConstraints(
// final IPredicate>[] path, final IConstraint[] joinGraphConstraints) {
//
// return getJoinGraphConstraints(path, joinGraphConstraints, null/*knownBound*/);
//
// }
/**
* Given a join path, return the set of constraints to be associated with
* each join in that join path. Only those constraints whose variables are
* known to be bound will be attached.
*
* @param path
* The join path.
* @param joinGraphConstraints
* The constraints to be applied to the join path (optional).
* @param knownBoundVars
* Variables that are known to be bound as inputs to this join
* graph (parent queries).
* @param pathIsComplete
* true
iff the path represents a complete
* join path. When true
, any constraints which have
* not already been attached will be attached to the last predicate
* in the join path.
*
* @return The constraints to be paired with each element of the join path.
*
* @throws IllegalArgumentException
* if the join path is null
.
* @throws IllegalArgumentException
* if the join path is empty.
* @throws IllegalArgumentException
* if any element of the join path is null
.
* @throws IllegalArgumentException
* if any element of the join graph constraints is
* null
.
*/
static public IConstraint[][] getJoinGraphConstraints(
final IPredicate>[] path,//
final IConstraint[] joinGraphConstraints,//
final IVariable>[] knownBoundVars,//
final boolean pathIsComplete//
) {
if (path == null)
throw new IllegalArgumentException();
if (path.length == 0)
throw new IllegalArgumentException();
// the set of constraints for each predicate in the join path.
final IConstraint[][] ret = new IConstraint[path.length][];
// the set of variables which are bound.
final Set> boundVars = new LinkedHashSet>();
// add the already known bound vars
if (knownBoundVars != null) {
for (IVariable> v : knownBoundVars)
boundVars.add(v);
}
/*
* For each predicate in the path in the given order, figure out which
* constraint(s) would attach to that predicate based on which variables
* first become bound with that predicate. For the last predicate in the
* given join path, we return that set of constraints.
*/
// the set of constraints which have been consumed.
final Set used = new LinkedHashSet();
for (int i = 0; i < path.length; i++) {
// // true iff this is the last join in the path.
// final boolean lastJoin = i == path.length - 1;
// a predicate in the path.
final IPredicate> p = path[i];
if (p == null)
throw new IllegalArgumentException();
// the constraints for the current predicate in the join path.
final List constraints = new LinkedList();
{
/*
* Visit the variables used by the predicate (and bound by it
* since it is not an optional predicate) and add them into the
* total set of variables which are bound at this point in the
* join path.
*/
final Iterator> vitr = BOpUtility
.getArgumentVariables(p);
while (vitr.hasNext()) {
final IVariable> var = vitr.next();
boundVars.add(var);
}
}
if (joinGraphConstraints != null) {
// consider each constraint.
for (IConstraint c : joinGraphConstraints) {
if (c == null)
throw new IllegalArgumentException();
if (used.contains(c)) {
/*
* Skip constraints which were already assigned to
* predicates before this one in the join path.
*/
continue;
}
boolean attach = false;
if (pathIsComplete && i == path.length - 1) {
// attach all unused constraints to last predicate
attach = true;
} else {
/*
* true iff all variables used by this constraint are bound
* at this point in the join path.
*/
boolean allVarsBound = true;
// visit the variables used by this constraint.
final Iterator> vitr = BOpUtility
.getSpannedVariables(c);
while (vitr.hasNext()) {
final IVariable> var = vitr.next();
if (!boundVars.contains(var)) {
allVarsBound = false;
break;
}
}
attach = allVarsBound;
}
if (attach) {
/*
* All variables have become bound for this constraint,
* so add it to the set of "used" constraints.
*/
used.add(c);
if (log.isDebugEnabled()) {
log.debug("Constraint attached at index " + i
+ " of " + path.length + ", bopId="
+ p.getId() + ", constraint=" + c);
}
constraints.add(c);
} // if(allVarsBound)
} // next constraint
} // joinGraphConstraints != null;
// store the constraint[] for that predicate.
ret[i] = constraints.toArray(new IConstraint[constraints.size()]);
} // next predicate in the join path.
/*
* Return the set of constraints associated with each predicate in the
* join path.
*/
return ret;
}
/**
* The {@link IPredicate}s representing optional joins. Any
* {@link IConstraint}s having variable(s) NOT bound by the required joins
* will already have been attached to the last {@link IPredicate} in the
* tail plan in which their variable(S) MIGHT have been bound.
*/
public IPredicate>[] getTailPlan() {
return tailPlan.toArray(new IPredicate[tailPlan.size()]);
}
/**
* Return the set of {@link IConstraint}s which should be evaluated when an
* identified predicate having SPARQL optional semantics is evaluated. For
* constraints whose variables are not known to be bound when entering the
* tail plan, the constraint should be evaluated at the last predicate for
* which its variables MIGHT become bound.
*
* @param bopId
* The identifier for an {@link IPredicate} appearing in the tail
* plan.
*
* @return The set of constraints to be imposed by the join which evaluates
* that predicate. This will be an empty array if there are no
* constraints which can be imposed when that predicate is
* evaluated.
*
* @throws IllegalArgumentException
* if there is no such predicate in the tail plan.
*/
public IConstraint[] getTailPlanConstraints(final int bopId) {
boolean found = false;
for (IPredicate> p : tailPlan) {
if (p.getId() == bopId) {
found = true;
break;
}
}
if (!found)
throw new IllegalArgumentException(
"No such predicate in tail plan: bopId=" + bopId);
final List constraints = tailPlanConstraintMap.get(bopId);
if (constraints == null) {
return new IConstraint[0];
}
return constraints.toArray(new IConstraint[constraints.size()]);
}
/**
* Return true
iff two predicates can join on the basis of at
* least one variable which is shared directly by those predicates. Only the
* operands of the predicates are considered.
*
* Note: This method will only identify joins where the predicates directly
* share at least one variable. However, joins are also possible when the
* predicates share variables via one or more constraint(s). Use
* {@link canJoinUsingConstraints} to identify such joins.
*
* Note: Any two predicates may join regardless of the presence of shared
* variables. However, such joins will produce the full cross product of the
* binding sets selected by each predicate. As such, they should be run last
* and this method will not return true
for such predicates.
*
* Note: This method is more efficient than
* {@link BOpUtility#getSharedVars(BOp, BOp)} because it does not
* materialize the sets of shared variables. However, it only considers the
* operands of the {@link IPredicate}s and is thus more restricted than
* {@link BOpUtility#getSharedVars(BOp, BOp)} as well.
*
* @param p1
* A predicate.
* @param p2
* Another predicate.
*
* @return true
iff the predicates share at least one variable
* as an operand.
*
* @throws IllegalArgumentException
* if the two either reference is null
.
*/
static public boolean canJoin(final IPredicate> p1, final IPredicate> p2) {
if (p1 == null)
throw new IllegalArgumentException();
if (p2 == null)
throw new IllegalArgumentException();
// iterator scanning the operands of p1.
final Iterator> itr1 = BOpUtility.getArgumentVariables(p1);
while (itr1.hasNext()) {
final IVariable> v1 = itr1.next();
// iterator scanning the operands of p2.
final Iterator> itr2 = BOpUtility
.getArgumentVariables(p2);
while (itr2.hasNext()) {
final IVariable> v2 = itr2.next();
if (v1 == v2) {
if (log.isDebugEnabled())
log.debug("Can join: sharedVar=" + v1 + ", p1=" + p1
+ ", p2=" + p2);
return true;
}
}
}
if (log.isDebugEnabled())
log.debug("No directly shared variable: p1=" + p1 + ", p2=" + p2);
return false;
}
/**
* Return true
iff a predicate may be used to extend a join
* path on the basis of at least one variable which is shared either
* directly or via one or more constraints which may be attached to the
* predicate when it is added to the join path. The join path is used to
* decide which variables are known to be bound, which in turn decides which
* constraints may be run. Unlike the case when the variable is directly
* shared between the two predicates, a join involving a constraint requires
* us to know which variables are already bound so we can know when the
* constraint may be attached.
*
* Note: Use {@link PartitionedJoinGroup#canJoin(IPredicate, IPredicate)}
* instead to identify joins based on a variable which is directly shared.
*
* Note: Any two predicates may join regardless of the presence of shared
* variables. However, such joins will produce the full cross product of the
* binding sets selected by each predicate. As such, they should be run last
* and this method will not return true
for such predicates.
*
* @param path
* A join path containing at least one predicate.
* @param vertex
* A predicate which is being considered as an extension of that
* join path.
* @param constraints
* A set of zero or more constraints (optional). Constraints are
* attached dynamically once the variables which they use are
* bound. Hence, a constraint will always share a variable with
* any predicate to which it is attached. If any constraints are
* attached to the given vertex and they share a variable which
* has already been bound by the join path, then the vertex may
* join with the join path even if it does not directly bind that
* variable.
*
* @return true
iff the vertex can join with the join path via
* a shared variable.
*
* @throws IllegalArgumentException
* if the join path is null
.
* @throws IllegalArgumentException
* if the join path is empty.
* @throws IllegalArgumentException
* if any element in the join path is null
.
* @throws IllegalArgumentException
* if the vertex is null
.
* @throws IllegalArgumentException
* if the vertex is already part of the join path.
* @throws IllegalArgumentException
* if any element in the optional constraints array is
* null
.
*/
static public boolean canJoinUsingConstraints(final IPredicate>[] path,
final IPredicate> vertex, final IConstraint[] constraints) {
/*
* Check arguments.
*/
if (path == null)
throw new IllegalArgumentException();
if (vertex == null)
throw new IllegalArgumentException();
// constraints MAY be null.
if (path.length == 0)
throw new IllegalArgumentException();
{
for (IPredicate> p : path) {
if (p == null)
throw new IllegalArgumentException();
if (vertex == p)
throw new IllegalArgumentException();
}
}
/*
* Find the set of variables which are known to be bound because they
* are referenced as operands of the predicates in the join path.
*/
final Set> knownBound = new LinkedHashSet>();
for (IPredicate> p : path) {
final Iterator> vitr = BOpUtility
.getArgumentVariables(p);
while (vitr.hasNext()) {
knownBound.add(vitr.next());
}
}
/*
*
* If the given predicate directly shares a variable with any of the
* predicates in the join path, then we can return immediately.
*/
{
final Iterator> vitr = BOpUtility
.getArgumentVariables(vertex);
while (vitr.hasNext()) {
final IVariable> var = vitr.next();
if (knownBound.contains(var)) {
if (log.isDebugEnabled())
log.debug("Can join: sharedVar=" + var + ", path="
+ Arrays.toString(path) + ", vertex=" + vertex);
return true;
}
}
}
if (constraints == null) {
// No opportunity for a constraint based join.
if (log.isDebugEnabled())
log.debug("No directly shared variable: path="
+ Arrays.toString(path) + ", vertex=" + vertex);
return false;
}
/*
* Find the set of constraints which can run with the vertex given the
* join path.
*/
{
// Extend the new join path.
final IPredicate>[] newPath = new IPredicate[path.length + 1];
System.arraycopy(path/* src */, 0/* srcPos */, newPath/* dest */,
0/* destPos */, path.length);
newPath[path.length] = vertex;
/*
* Find the constraints that will run with each vertex of the new
* join path.
*/
final IConstraint[][] constraintRunArray = getJoinGraphConstraints(
newPath, constraints, null/*knownBound*/,
true/*pathIsComplete*/
);
/*
* Consider only the constraints attached to the last vertex in the
* new join path. All of their variables will be bound since (by
* definition) a constraint may not run until its variables are
* bound. If any of the constraints attached to that last share any
* variables which were already known to be bound in the caller's
* join path, then the vertex can join (without of necessity being a
* full cross product join).
*/
final IConstraint[] vertexConstraints = constraintRunArray[path.length];
for (IConstraint c : vertexConstraints) {
// consider all variables spanned by the constraint.
final Iterator> vitr = BOpUtility
.getSpannedVariables(c);
while (vitr.hasNext()) {
final IVariable> var = vitr.next();
if (knownBound.contains(var)) {
if (log.isDebugEnabled())
log.debug("Can join: sharedVar=" + var + ", path="
+ Arrays.toString(path) + ", vertex="
+ vertex + ", constraint=" + c);
return true;
}
}
}
}
if (log.isDebugEnabled())
log.debug("No shared variable: path=" + Arrays.toString(path)
+ ", vertex=" + vertex + ", constraints="
+ Arrays.toString(constraints));
return false;
}
/**
* Analyze a set of {@link IPredicate}s representing "runFirst", optional
* joins, and non-optional joins which may be freely reordered together with
* a collection of {@link IConstraint}s and partition them into a join graph
* and a tail plan. The resulting data structure can efficiently answer a
* variety of queries regarding joins, join paths, and constraints and can
* be used to formulate a complete query when combined with a desired join
* ordering.
*
* @param knownBound
* A set of variables which are known to be bound on entry.
* @param sourcePreds
* The predicates.
* @param constraints
* The constraints.
*
* @return A data structure representing both the join graph and the tail
* plan.
*
* @throws IllegalArgumentException
* if the source predicates array is null
.
* @throws IllegalArgumentException
* if the source predicates array is empty.
* @throws IllegalArgumentException
* if any element of the source predicates array is
* null
.
*/
public PartitionedJoinGroup(//
// final Set> knownBound,
final IPredicate>[] sourcePreds,//
IConstraint[] constraints) {
if (sourcePreds == null)
throw new IllegalArgumentException();
if (sourcePreds.length == 0)
throw new IllegalArgumentException();
if (constraints == null) {
// replace with an empty array.
constraints = IConstraint.EMPTY;
}
/*
* First identify the predicates which correspond to non-optional joins.
* All other pipeline operators are inserted into the tail plan in the
* order in which they are given.
*/
for (IPredicate> p : sourcePreds) {
if (p == null)
throw new IllegalArgumentException();
if (p.isOptional()) {
if (p.getAccessPathExpander() != null
&& p.getAccessPathExpander().runFirst())
throw new IllegalStateException(
"runFirst is not compatible with optional: " + p);
// an optional predicate
tailPlan.add(p);
} else {
// non-optional predicate.
if (p.getAccessPathExpander() != null
&& p.getAccessPathExpander().runFirst()) {
headPlan.add(p);
} else {
// part of the join graph.
joinGraph.add(p);
}
/*
* Add to the set of variables which will be bound by the time
* the join graph is done executing.
*/
final Iterator> vitr = BOpUtility
.getArgumentVariables(p);
while (vitr.hasNext()) {
joinGraphVars.add(vitr.next());
}
}
}
/*
* Now break the constraints into different groups based on their
* variables and when those variables are known to be bound (required
* joins) or might be bound (optionals).
*/
for (IConstraint c : constraints) {
boolean allFound = true;
final Iterator> vitr = BOpUtility
.getSpannedVariables(c);
if (!vitr.hasNext()) {
/*
* All constraints should have at least one variable.
*/
throw new RuntimeException("No variables in constraint: " + c);
}
while (vitr.hasNext()) {
final IVariable> var = vitr.next();
if (!joinGraphVars.contains(var)) {
/*
* This constraint will be evaluated against the tail plan.
*/
allFound = false;
tailPlanConstraints.add(c);
break;
}
}
if (allFound) {
/*
* This constraint will be evaluated by the join graph for the
* first join in in which all of the variables used by the
* constraint are known to be bound.
*/
joinGraphConstraints.add(c);
}
}
/*
* If a variable is not bound by a required predicate, then we attach
* any constraint using that variable to the last optional predicate in
* which that variable MIGHT become bound.
*/
{
/*
* Populate a map from each variable not bound in the join graph to
* the last index in the tail plan at which it MIGHT become bound.
*/
final Map, Integer/* lastIndexOf */> lastIndexOf = new LinkedHashMap, Integer>();
int indexOf = 0;
for (IPredicate> p : tailPlan) {
final Iterator> vitr = BOpUtility
.getArgumentVariables(p);
while (vitr.hasNext()) {
final IVariable> var = vitr.next();
lastIndexOf.put(var, Integer.valueOf(indexOf));
}
indexOf++;
}
/*
* For each constraint using at least one variable NOT bound by the
* join graph, find the maximum value of lastIndexOf for the
* variable(s) in that constraint. That is the index of the operator
* in the tail plan to which the constraint should be attached.
*/
for (IConstraint c : tailPlanConstraints) {
final Iterator> vitr = BOpUtility
.getSpannedVariables(c);
Integer maxLastIndexOf = null;
while (vitr.hasNext()) {
final IVariable> var = vitr.next();
if (joinGraphVars.contains(var)) {
// This variable is bound by the join graph.
continue;
}
final Integer tmp = lastIndexOf.get(var);
if (tmp == null) {
// This variable is never bound by the query.
throw new NoSolutionsException(
"Variable is never bound: " + var);
}
if (maxLastIndexOf == null
|| tmp.intValue() > maxLastIndexOf.intValue()) {
maxLastIndexOf = tmp;
}
} // next variable.
if (maxLastIndexOf == null) {
// A logic error.
throw new AssertionError("maxLastIndexOf is undefined: "
+ c);
}
/*
* Add the constraint to the last predicate at which any of its
* variables MIGHT have become bound.
*/
{
/*
* The bopId for the predicate in the tail plan for which,
* when that predicate is evaluated, we will run this
* constraint.
*/
final int predId = tailPlan.get(maxLastIndexOf).getId();
/*
* The constraint(s) (if any) already associated with that
* predicate.
*/
List tmp = tailPlanConstraintMap.get(predId);
if (tmp == null) {
tmp = new LinkedList();
tailPlanConstraintMap.put(predId, tmp);
}
tmp.add(c);
}
} // next tail plan constraint.
}
}
// /**
// * Generate a query plan from an ordered collection of predicates.
// *
// * @param distinct
// * true
iff only the distinct solutions are desired.
// * @param selected
// * The variable(s) to be projected out of the join graph.
// * @param preds
// * The join path which will be used to execute the join graph.
// * @param constraints
// * The constraints on the join graph.
// *
// * @return The query plan.
// *
// * FIXME Select only those variables required by downstream
// * processing or explicitly specified by the caller (in the case
// * when this is a subquery, the caller has to declare which
// * variables are selected and will be returned out of the subquery).
// *
// * FIXME For scale-out, we need to either mark the join's evaluation
// * context based on whether or not the access path is local or
// * remote (and whether the index is key-range distributed or hash
// * partitioned).
// *
// * FIXME Add a method to generate a runnable query plan from the
// * collection of predicates and constraints on the
// * {@link PartitionedJoinGroup} together with an ordering over the
// * join graph. This is a bit different for the join graph and the
// * optionals in the tail plan. The join graph itself should either
// * be a {@link JoinGraph} operator which gets evaluated at run time
// * or reordered by whichever optimizer is selected for the query
// * (query hints).
// *
// * @todo The order of the {@link IPredicate}s in the tail plan is currently
// * unchanged from their given order (optional joins without
// * constraints can not reduce the selectivity of the query). However,
// * it could be worthwhile to run optionals with constraints before
// * those without constraints since the constraints can reduce the
// * selectivity of the query. If we do this, then we need to reorder
// * the optionals based on the partial order imposed what variables
// * they MIGHT bind (which are not bound by the join graph).
// *
// * @todo multiple runFirst predicates can be evaluated in parallel unless
// * they have shared variables. When there are no shared variables,
// * construct a TEE pattern such that evaluation proceeds in parallel.
// * When there are shared variables, the runFirst predicates must be
// * ordered based on those shared variables (at which point, it is
// * probably an error to flag them as runFirst).
// */
// static public PipelineOp getQuery(final BOpIdFactory idFactory,
// final boolean distinct, final IVariable>[] selected,
// final IPredicate>[] preds, final IConstraint[] constraints) {
//
// /*
// * Reserve ids used by the join graph or its constraints.
// */
// idFactory.reserveIds(preds, constraints);
//// {
//// for (IPredicate> p : preds) {
//// idFactory.reserve(p.getId());
//// }
//// if (constraints != null) {
//// for (IConstraint c : constraints) {
//// final Iterator itr = BOpUtility
//// .preOrderIteratorWithAnnotations(c);
//// while (itr.hasNext()) {
//// final BOp y = itr.next();
//// final Integer anId = (Integer) y
//// .getProperty(BOp.Annotations.BOP_ID);
//// if (anId != null)
//// idFactory.reserve(anId.intValue());
//// }
//// }
//// }
//// }
//
// // figure out which constraints are attached to which predicates.
// final IConstraint[][] assignedConstraints = PartitionedJoinGroup
// .getJoinGraphConstraints(preds, constraints, null/*knownBound*/,
// true/*pathIsComplete*/);
//
//// final PipelineJoin>[] joins = new PipelineJoin[preds.length];
//
// PipelineOp lastOp = null;
//
// final Set> knownBound = new LinkedHashSet>();
//
// for (int i = 0; i < preds.length; i++) {
//
// // The next vertex in the selected join order.
// final IPredicate> p = preds[i];
//
// // Annotations for this join.
// final List anns = new LinkedList();
//
// anns.add(new NV(PipelineJoin.Annotations.PREDICATE, p));
//
// anns.add(new NV(PipelineJoin.Annotations.BOP_ID, idFactory
// .nextId()));
//
//// anns.add(new NV(PipelineJoin.Annotations.EVALUATION_CONTEXT, BOpEvaluationContext.ANY));
////
//// anns.add(new NV(PipelineJoin.Annotations.SELECT, vars.toArray(new IVariable[vars.size()])));
//
// if (assignedConstraints[i] != null
// && assignedConstraints[i].length > 0) {
// // attach constraints to this join.
// anns.add(new NV(PipelineJoin.Annotations.CONSTRAINTS,
// assignedConstraints[i]));
// }
//
// // collect variables used as arguments by this predicate.
// final Set> pvars = new LinkedHashSet>();
// {
// final Iterator> vitr = BOpUtility
// .getArgumentVariables(p);
// while (vitr.hasNext()) {
// pvars.add(vitr.next());
// }
// }
//
// // figure out if there are ANY shared variables.
// boolean shared = false;
// {
// for(IVariable> v : pvars) {
// if(knownBound.contains(v)) {
// shared = true;
// break;
// }
// }
// }
//
// /*
// * FIXME Explore the merit of this optimization with MikeP,
// * including consideration of the PIPELINE_QUEUE_CAPACITY and
// * whether or not to request an analytic join (hash join).
// */
// if (false && !shared) {
// System.err.println("Full cross product join: " + p);
// /*
// * Force at-once evaluation to ensure that we evaluate the AP
// * for [p] exactly once.
// */
// anns.add(new NV(PipelineOp.Annotations.PIPELINED, false));
// }
//
// final PipelineJoin> joinOp = new PipelineJoin(//
// lastOp == null ? new BOp[0] : new BOp[] { lastOp }, //
// anns.toArray(new NV[anns.size()])//
// );
//
// // Add predicate argument variables to [knownBound].
// knownBound.addAll(pvars);
//
// lastOp = joinOp;
//
// }
//
// if (distinct) {
// lastOp = new JVMDistinctBindingSetsOp(new BOp[] { lastOp }, NV
// .asMap(new NV[] {
// new NV(PipelineOp.Annotations.BOP_ID, idFactory
// .nextId()), //
// new NV(PipelineOp.Annotations.EVALUATION_CONTEXT,
// BOpEvaluationContext.CONTROLLER),//
// new NV(PipelineOp.Annotations.SHARED_STATE, true),//
// new NV(JVMDistinctBindingSetsOp.Annotations.VARIABLES,
// selected),//
// })//
// );
// }
//
// return lastOp;
//
// }
}