use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class PlanAssembler method simplifyOuterJoin.
/**
* Outer join simplification using null rejection.
* http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.43.2531
* Outerjoin Simplification and Reordering for Query Optimization
* by Cesar A. Galindo-Legaria , Arnon Rosenthal
* Algorithm:
* Traverse the join tree top-down:
* For each join node n1 do:
* For each expression expr (join and where) at the node n1
* For each join node n2 descended from n1 do:
* If expr rejects nulls introduced by n2 inner table, then
* - convert LEFT OUTER n2 to an INNER join.
* - convert FULL OUTER n2 to RIGHT OUTER join
* If expr rejects nulls introduced by n2 outer table, then
* - convert RIGHT OUTER n2 to an INNER join.
* - convert FULL OUTER n2 to LEFT OUTER join
*/
private static void simplifyOuterJoin(BranchNode joinTree) {
assert (joinTree != null);
List<AbstractExpression> exprs = new ArrayList<>();
JoinNode leftNode = joinTree.getLeftNode();
JoinNode rightNode = joinTree.getRightNode();
// WHERE expressions need to be evaluated for NULL-rejection
if (leftNode.getWhereExpression() != null) {
exprs.add(leftNode.getWhereExpression());
}
if (rightNode.getWhereExpression() != null) {
exprs.add(rightNode.getWhereExpression());
}
simplifyOuterJoinRecursively(joinTree, exprs);
}
use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class PlanAssembler method calculateGroupbyColumnsCovered.
private List<Integer> calculateGroupbyColumnsCovered(Index index, String fromTableAlias, List<AbstractExpression> bindings) {
List<Integer> coveredGroupByColumns = new ArrayList<>();
List<ParsedColInfo> groupBys = m_parsedSelect.groupByColumns();
String exprsjson = index.getExpressionsjson();
if (exprsjson.isEmpty()) {
List<ColumnRef> indexedColRefs = CatalogUtil.getSortedCatalogItems(index.getColumns(), "index");
for (int j = 0; j < indexedColRefs.size(); j++) {
String indexColumnName = indexedColRefs.get(j).getColumn().getName();
// ignore order of keys in GROUP BY expr
int ithCovered = 0;
boolean foundPrefixedColumn = false;
for (; ithCovered < groupBys.size(); ithCovered++) {
AbstractExpression gbExpr = groupBys.get(ithCovered).expression;
if (!(gbExpr instanceof TupleValueExpression)) {
continue;
}
TupleValueExpression gbTVE = (TupleValueExpression) gbExpr;
// TVE column index has not been resolved currently
if (fromTableAlias.equals(gbTVE.getTableAlias()) && indexColumnName.equals(gbTVE.getColumnName())) {
foundPrefixedColumn = true;
break;
}
}
if (!foundPrefixedColumn) {
// no prefix match any more
break;
}
coveredGroupByColumns.add(ithCovered);
if (coveredGroupByColumns.size() == groupBys.size()) {
// covered all group by columns already
break;
}
}
} else {
StmtTableScan fromTableScan = m_parsedSelect.getStmtTableScanByAlias(fromTableAlias);
// either pure expression index or mix of expressions and simple columns
List<AbstractExpression> indexedExprs = null;
try {
indexedExprs = AbstractExpression.fromJSONArrayString(exprsjson, fromTableScan);
} catch (JSONException e) {
e.printStackTrace();
// This case sounds impossible
return coveredGroupByColumns;
}
for (AbstractExpression indexExpr : indexedExprs) {
// ignore order of keys in GROUP BY expr
List<AbstractExpression> binding = null;
for (int ithCovered = 0; ithCovered < groupBys.size(); ithCovered++) {
AbstractExpression gbExpr = groupBys.get(ithCovered).expression;
binding = gbExpr.bindingToIndexedExpression(indexExpr);
if (binding != null) {
bindings.addAll(binding);
coveredGroupByColumns.add(ithCovered);
break;
}
}
// no prefix match any more or covered all group by columns already
if (binding == null || coveredGroupByColumns.size() == groupBys.size()) {
break;
}
}
}
return coveredGroupByColumns;
}
use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class PlanAssembler method handleWindowedOperators.
/**
* Create nodes for windowed operations.
*
* @param root
* @return
*/
private AbstractPlanNode handleWindowedOperators(AbstractPlanNode root) {
// Get the windowed expression. We need to set its output
// schema from the display list.
WindowFunctionExpression winExpr = m_parsedSelect.getWindowFunctionExpressions().get(0);
assert (winExpr != null);
// This will set the output schema to contain the
// windowed schema column only. In generateOutputSchema
// we will add the input columns.
WindowFunctionPlanNode pnode = new WindowFunctionPlanNode();
pnode.setWindowFunctionExpression(winExpr);
// We always need an order by plan node, even if the sort
// is optimized away by an index. This may be turned
// into an inline order by in a MergeReceivePlanNode.
IndexUseForOrderBy scanNode = findScanNodeForWindowFunction(root);
AbstractPlanNode cnode = null;
int winfunc = (scanNode == null) ? SubPlanAssembler.NO_INDEX_USE : scanNode.getWindowFunctionUsesIndex();
// statement level order by ordering.
if ((SubPlanAssembler.STATEMENT_LEVEL_ORDER_BY_INDEX == winfunc) || (SubPlanAssembler.NO_INDEX_USE == winfunc)) {
// No index. Calculate the expression order here and stuff it into
// the order by node. Note that if we support more than one window
// function this would be the case when scanNode.getWindowFunctionUsesIndex()
// returns a window function number which is different from the number
// of winExpr.
List<AbstractExpression> partitionByExpressions = winExpr.getPartitionByExpressions();
// If the order by expression list contains a partition by expression then
// we won't have to sort by it twice. We sort by the partition by expressions
// first, and we don't care what order we sort by them. So, find the
// sort direction in the order by list and use that in the partition by
// list, and then mark that it was deleted in the order by
// list.
//
// We choose to make this dontsort rather than dosort because the
// Java default value for boolean is false, and we want to sort by
// default.
boolean[] dontsort = new boolean[winExpr.getOrderbySize()];
List<AbstractExpression> orderByExpressions = winExpr.getOrderByExpressions();
List<SortDirectionType> orderByDirections = winExpr.getOrderByDirections();
OrderByPlanNode onode = new OrderByPlanNode();
for (int idx = 0; idx < winExpr.getPartitionbySize(); ++idx) {
SortDirectionType pdir = SortDirectionType.ASC;
AbstractExpression partitionByExpression = partitionByExpressions.get(idx);
int sidx = winExpr.getSortIndexOfOrderByExpression(partitionByExpression);
if (0 <= sidx) {
pdir = orderByDirections.get(sidx);
dontsort[sidx] = true;
}
onode.addSort(partitionByExpression, pdir);
}
for (int idx = 0; idx < winExpr.getOrderbySize(); ++idx) {
if (!dontsort[idx]) {
AbstractExpression orderByExpr = orderByExpressions.get(idx);
SortDirectionType orderByDir = orderByDirections.get(idx);
onode.addSort(orderByExpr, orderByDir);
}
}
onode.addAndLinkChild(root);
cnode = onode;
} else {
assert (scanNode != null);
// inline order by node of a MergeReceive node.
assert (0 == scanNode.getWindowFunctionUsesIndex());
if (m_partitioning.requiresTwoFragments()) {
OrderByPlanNode onode = new OrderByPlanNode();
SortDirectionType dir = scanNode.getSortOrderFromIndexScan();
assert (dir != SortDirectionType.INVALID);
// This was created when the index was determined.
// We cached it in the scan node.
List<AbstractExpression> orderExprs = scanNode.getFinalExpressionOrderFromIndexScan();
assert (orderExprs != null);
for (AbstractExpression ae : orderExprs) {
onode.addSort(ae, dir);
}
// Link in the OrderByNode.
onode.addAndLinkChild(root);
cnode = onode;
} else {
// Don't create and link in the order by node.
cnode = root;
}
}
pnode.addAndLinkChild(cnode);
return pnode;
}
use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class SelectSubPlanAssembler method generateInnerAccessPaths.
/**
* Generate all possible access paths for an inner node in a join.
* The set of potential index expressions depends whether the inner node can be inlined
* with the NLIJ or not. In the former case, inner and inner-outer join expressions can
* be considered for the index access. In the latter, only inner join expressions qualifies.
*
* @param parentNode A parent node to the node to generate paths to.
*/
private void generateInnerAccessPaths(BranchNode parentNode) {
JoinNode innerChildNode = parentNode.getRightNode();
assert (innerChildNode != null);
// In case of inner join WHERE and JOIN expressions can be merged
if (parentNode.getJoinType() == JoinType.INNER) {
parentNode.m_joinInnerOuterList.addAll(parentNode.m_whereInnerOuterList);
parentNode.m_whereInnerOuterList.clear();
parentNode.m_joinInnerList.addAll(parentNode.m_whereInnerList);
parentNode.m_whereInnerList.clear();
}
if (innerChildNode instanceof BranchNode) {
generateOuterAccessPaths((BranchNode) innerChildNode);
generateInnerAccessPaths((BranchNode) innerChildNode);
// The inner node is a join node itself. Only naive access path is possible
innerChildNode.m_accessPaths.add(getRelevantNaivePath(parentNode.m_joinInnerOuterList, parentNode.m_joinInnerList));
return;
}
// The inner table can have multiple index access paths based on
// inner and inner-outer join expressions plus the naive one.
List<AbstractExpression> filterExprs = null;
List<AbstractExpression> postExprs = null;
// the inner join expression will effectively filter out inner tuple prior to the NLJ.
if (parentNode.getJoinType() != JoinType.FULL) {
filterExprs = parentNode.m_joinInnerList;
} else {
postExprs = parentNode.m_joinInnerList;
}
StmtTableScan innerTable = innerChildNode.getTableScan();
assert (innerTable != null);
innerChildNode.m_accessPaths.addAll(getRelevantAccessPathsForTable(innerTable, parentNode.m_joinInnerOuterList, filterExprs, postExprs));
// If there are inner expressions AND inner-outer expressions, it could be that there
// are indexed access paths that use elements of both in the indexing expressions,
// especially in the case of a compound index.
// These access paths can not be considered for use with an NLJ because they rely on
// inner-outer expressions.
// If there is a possibility that NLIJ will not be an option due to the
// "special case" processing that puts a send/receive plan between the join node
// and its inner child node, other access paths need to be considered that use the
// same indexes as those identified so far but in a simpler, less effective way
// that does not rely on inner-outer expressions.
// The following simplistic method of finding these access paths is to force
// inner-outer expressions to be handled as NLJ-compatible post-filters and repeat
// the search for access paths.
// This will typically generate some duplicate access paths, including the naive
// sequential scan path and any indexed paths that happened to use only the inner
// expressions.
// For now, we deal with this redundancy by dropping (and re-generating) all
// access paths EXCPT those that reference the inner-outer expressions.
// TODO: implementing access path hash and equality and possibly using a "Set"
// would allow deduping as new access paths are added OR
// the simplified access path search process could be based on
// the existing indexed access paths -- for each access path that "hasInnerOuterIndexExpression"
// try to generate and add a simpler access path using the same index,
// this time with the inner-outer expressions used only as non-indexable post-filters.
// Don't bother generating these redundant or inferior access paths unless there is
// an inner-outer expression and a chance that NLIJ will be taken out of the running.
boolean mayNeedInnerSendReceive = (!m_partitioning.wasSpecifiedAsSingle()) && (m_partitioning.getCountOfPartitionedTables() > 0) && (parentNode.getJoinType() != JoinType.INNER) && !innerTable.getIsReplicated();
if (mayNeedInnerSendReceive && !parentNode.m_joinInnerOuterList.isEmpty()) {
List<AccessPath> innerOuterAccessPaths = new ArrayList<>();
for (AccessPath innerAccessPath : innerChildNode.m_accessPaths) {
if ((innerAccessPath.index != null) && hasInnerOuterIndexExpression(innerChildNode.getTableAlias(), innerAccessPath.indexExprs, innerAccessPath.initialExpr, innerAccessPath.endExprs)) {
innerOuterAccessPaths.add(innerAccessPath);
}
}
if (parentNode.getJoinType() != JoinType.FULL) {
filterExprs = parentNode.m_joinInnerList;
postExprs = parentNode.m_joinInnerOuterList;
} else {
// For FULL join type the inner join expressions must be part of the post predicate
// in order to stay at the join node and not be pushed down to the inner node
filterExprs = null;
postExprs = new ArrayList<>(parentNode.m_joinInnerList);
postExprs.addAll(parentNode.m_joinInnerOuterList);
}
Collection<AccessPath> nljAccessPaths = getRelevantAccessPathsForTable(innerTable, null, filterExprs, postExprs);
innerChildNode.m_accessPaths.clear();
innerChildNode.m_accessPaths.addAll(nljAccessPaths);
innerChildNode.m_accessPaths.addAll(innerOuterAccessPaths);
}
assert (innerChildNode.m_accessPaths.size() > 0);
}
use of org.voltdb.expressions.AbstractExpression in project voltdb by VoltDB.
the class PlanAssembler method indexAccessForGroupByExprs.
// Turn sequential scan to index scan for group by if possible
private AbstractPlanNode indexAccessForGroupByExprs(SeqScanPlanNode root, IndexGroupByInfo gbInfo) {
if (root.isSubQuery()) {
// sub-query edge case will not be handled now
return root;
}
String fromTableAlias = root.getTargetTableAlias();
assert (fromTableAlias != null);
List<ParsedColInfo> groupBys = m_parsedSelect.groupByColumns();
Table targetTable = m_catalogDb.getTables().get(root.getTargetTableName());
assert (targetTable != null);
CatalogMap<Index> allIndexes = targetTable.getIndexes();
List<Integer> maxCoveredGroupByColumns = new ArrayList<>();
ArrayList<AbstractExpression> maxCoveredBindings = null;
Index pickedUpIndex = null;
boolean foundAllGroupByCoveredIndex = false;
for (Index index : allIndexes) {
if (!IndexType.isScannable(index.getType())) {
continue;
}
if (!index.getPredicatejson().isEmpty()) {
// do not try to look at Partial/Sparse index
continue;
}
ArrayList<AbstractExpression> bindings = new ArrayList<>();
List<Integer> coveredGroupByColumns = calculateGroupbyColumnsCovered(index, fromTableAlias, bindings);
if (coveredGroupByColumns.size() > maxCoveredGroupByColumns.size()) {
maxCoveredGroupByColumns = coveredGroupByColumns;
pickedUpIndex = index;
maxCoveredBindings = bindings;
if (maxCoveredGroupByColumns.size() == groupBys.size()) {
foundAllGroupByCoveredIndex = true;
break;
}
}
}
if (pickedUpIndex == null) {
return root;
}
IndexScanPlanNode indexScanNode = new IndexScanPlanNode(root, null, pickedUpIndex, SortDirectionType.INVALID);
indexScanNode.setForGroupingOnly();
indexScanNode.setBindings(maxCoveredBindings);
gbInfo.m_coveredGroupByColumns = maxCoveredGroupByColumns;
gbInfo.m_canBeFullySerialized = foundAllGroupByCoveredIndex;
return indexScanNode;
}
Aggregations