use of org.voltdb.planner.parseinfo.JoinNode in project voltdb by VoltDB.
the class SelectSubPlanAssembler method queueSubJoinOrders.
private static void queueSubJoinOrders(List<List<JoinNode>> joinOrderList, int joinOrderListIdx, ArrayList<JoinNode> currentJoinOrder, ArrayDeque<JoinNode> joinOrders, boolean findAll) {
if (!findAll && joinOrders.size() > 0) {
// At least find one valid join order
return;
}
if (joinOrderListIdx == joinOrderList.size()) {
// End of recursion
assert (!currentJoinOrder.isEmpty());
JoinNode joinTree = JoinNode.reconstructJoinTreeFromSubTrees(currentJoinOrder);
joinOrders.add(joinTree);
return;
}
// Recursive step
List<JoinNode> nextTrees = joinOrderList.get(joinOrderListIdx);
for (JoinNode headTree : nextTrees) {
ArrayList<JoinNode> updatedJoinOrder = new ArrayList<>();
// Order is important: The top sub-trees must be first
for (JoinNode node : currentJoinOrder) {
updatedJoinOrder.add((JoinNode) node.clone());
}
updatedJoinOrder.add((JoinNode) headTree.clone());
queueSubJoinOrders(joinOrderList, joinOrderListIdx + 1, updatedJoinOrder, joinOrders, findAll);
}
}
use of org.voltdb.planner.parseinfo.JoinNode in project voltdb by VoltDB.
the class SelectSubPlanAssembler method generateFullJoinOrdersForTree.
/**
* Helper method to generate join orders for a join tree containing only FULL joins.
* The only allowed permutation is a join order that has original left and right nodes
* swapped.
*
* @param subTree join tree
* @return list of valid join orders
*/
private static List<JoinNode> generateFullJoinOrdersForTree(JoinNode subTree) {
assert (subTree != null);
List<JoinNode> joinOrders = new ArrayList<>();
if (!(subTree instanceof BranchNode)) {
// End of recursion
joinOrders.add(subTree);
return joinOrders;
}
BranchNode branchNode = (BranchNode) subTree;
// Descend to the left branch
assert (branchNode.getLeftNode() != null);
List<JoinNode> leftJoinOrders = generateFullJoinOrdersForTree(branchNode.getLeftNode());
assert (!leftJoinOrders.isEmpty());
// Descend to the right branch
assert (branchNode.getRightNode() != null);
List<JoinNode> rightJoinOrders = generateFullJoinOrdersForTree(branchNode.getRightNode());
assert (!rightJoinOrders.isEmpty());
// Create permutation pairing left and right nodes and the revere variant
for (JoinNode leftNode : leftJoinOrders) {
for (JoinNode rightNode : rightJoinOrders) {
JoinNode resultOne = new BranchNode(branchNode.getId(), branchNode.getJoinType(), (JoinNode) leftNode.clone(), (JoinNode) rightNode.clone());
JoinNode resultTwo = new BranchNode(branchNode.getId(), branchNode.getJoinType(), (JoinNode) rightNode.clone(), (JoinNode) leftNode.clone());
if (branchNode.getJoinExpression() != null) {
resultOne.setJoinExpression(branchNode.getJoinExpression().clone());
resultTwo.setJoinExpression(branchNode.getJoinExpression().clone());
}
if (branchNode.getWhereExpression() != null) {
resultOne.setWhereExpression(branchNode.getWhereExpression().clone());
resultTwo.setWhereExpression(branchNode.getWhereExpression().clone());
}
joinOrders.add(resultOne);
joinOrders.add(resultTwo);
}
}
return joinOrders;
}
use of org.voltdb.planner.parseinfo.JoinNode in project voltdb by VoltDB.
the class SelectSubPlanAssembler method nextPlan.
/**
* Pull a join order out of the join orders deque, compute all possible plans
* for that join order, then append them to the computed plans deque.
*/
@Override
protected AbstractPlanNode nextPlan() {
// or no more plans can be created
while (m_plans.size() == 0) {
// get the join order for us to make plans out of
JoinNode joinTree = m_joinOrders.poll();
// no more join orders => no more plans to generate
if (joinTree == null) {
return null;
}
// Analyze join and filter conditions
joinTree.analyzeJoinExpressions(m_parsedStmt.m_noTableSelectionList);
// a query that is a little too quirky or complicated.
if (!m_parsedStmt.m_noTableSelectionList.isEmpty()) {
throw new PlanningErrorException("Join with filters that do not depend on joined tables is not supported in VoltDB");
}
if (!m_partitioning.wasSpecifiedAsSingle()) {
// Now that analyzeJoinExpressions has done its job of properly categorizing
// and placing the various filters that the HSQL parser tends to leave in the strangest
// configuration, this is the first opportunity to analyze WHERE and JOIN filters'
// effects on statement partitioning.
// But this causes the analysis to be run based on a particular join order.
// Which join orders does this analysis actually need to be run on?
// Can it be run on the first join order and be assumed to hold for all join orders?
// If there is a join order that fails to generate a single viable plan, is its
// determination of partitioning (or partitioning failure) always valid for other
// join orders, or should the analysis be repeated on a viable join order
// in that case?
// For now, analyze each join order independently and when an invalid partitioning is
// detected, skip the plan generation for that particular ordering.
// If this causes all plans to be skipped, commonly the case, the PlanAssembler
// should propagate an error message identifying partitioning as the problem.
HashMap<AbstractExpression, Set<AbstractExpression>> valueEquivalence = joinTree.getAllEquivalenceFilters();
Collection<StmtTableScan> scans = m_parsedStmt.allScans();
m_partitioning.analyzeForMultiPartitionAccess(scans, valueEquivalence);
if (!m_partitioning.isJoinValid()) {
// The case of more than one independent partitioned table
// would result in an illegal plan with more than two fragments.
// Don't throw a planning error here, in case the problem is just with this
// particular join order, but do leave a hint to the PlanAssembler in case
// the failure is unanimous -- a common case.
m_recentErrorMsg = m_partitioning.getJoinInvalidReason();
// This join order, at least, is not worth trying to plan.
continue;
}
}
generateMorePlansForJoinTree(joinTree);
}
return m_plans.poll();
}
use of org.voltdb.planner.parseinfo.JoinNode in project voltdb by VoltDB.
the class SelectSubPlanAssembler method generateOuterAccessPaths.
/**
* Generate all possible access paths for an outer node in a join.
* The outer table and/or join can have the naive access path and possible index path(s)
* Optimizations - outer-table-only where expressions can be pushed down to the child node
* to pre-qualify the outer tuples before they enter the join.
* For inner joins outer-table-only join expressions can be pushed down as well
*
* @param parentNode A parent node to the node to generate paths to.
*/
private void generateOuterAccessPaths(BranchNode parentNode) {
JoinNode outerChildNode = parentNode.getLeftNode();
assert (outerChildNode != null);
JoinType joinType = parentNode.getJoinType();
// For LEFT and FULL join types, the outer join expressions are kept as a pre-join predicate
// at the join node to pre-qualify the outer rows
List<AbstractExpression> joinOuterList = (joinType == JoinType.INNER) ? parentNode.m_joinOuterList : null;
if (outerChildNode instanceof BranchNode) {
generateOuterAccessPaths((BranchNode) outerChildNode);
generateInnerAccessPaths((BranchNode) outerChildNode);
// The join node can have only sequential scan access
outerChildNode.m_accessPaths.add(getRelevantNaivePath(joinOuterList, parentNode.m_whereOuterList));
assert (outerChildNode.m_accessPaths.size() > 0);
return;
}
// WHERE Outer expressions must stay at the join node for the FULL joins
// They will be added later as part of the WHERE predicate of the join node
List<AbstractExpression> parentWhereList = null;
if (joinType != JoinType.FULL) {
parentWhereList = parentNode.m_whereOuterList;
}
outerChildNode.m_accessPaths.addAll(getRelevantAccessPathsForTable(outerChildNode.getTableScan(), joinOuterList, parentWhereList, null));
}
Aggregations