if (sortDirection == SortDirectionType.ASC) {
assert(aggplan.isTableMin());
ispn.setSkipNullPredicate(0);
}
LimitPlanNode lpn = new LimitPlanNode();
lpn.setLimit(1);
lpn.setOffset(0);
ispn.addInlinePlanNode(lpn);
// remove old SeqScan node and link the new generated IndexScan node
plan.clearChildren();
plan.addAndLinkChild(ispn);
return plan;
}
}
if ((child instanceof IndexScanPlanNode) == false) {
return plan;
}
// already have the IndexScanPlanNode
IndexScanPlanNode ispn = (IndexScanPlanNode)child;
// can do optimization only if it has no (post-)predicates
// except those (post-)predicates are artifact predicates
// we added for reverse scan purpose only
if (((IndexScanPlanNode)child).getPredicate() != null &&
!((IndexScanPlanNode)child).isPredicatesOptimizableForAggregate()) {
return plan;
}
// Guard against (possible future?) cases of indexable subquery.
if (((AbstractScanPlanNode)child).isSubQuery()) {
return plan;
}
// 1. Handle ALL equality filters case.
// In the IndexScanPlanNode:
// -- EQFilterExprs were put in searchkeyExpressions and endExpressions
// -- startCondition is only in searchKeyExpressions
// -- endCondition is only in endExpressions
// So, if the lookup type is EQ, then all filters must be equality; or if
// there are extra startCondition / endCondition, some filters are not equality
// 2. Handle equality filters and one other comparison operator (<, <=, >, >=), see comments below
if (ispn.getLookupType() != IndexLookupType.EQ &&
Math.abs(ispn.getSearchKeyExpressions().size() - ExpressionUtil.uncombine(ispn.getEndExpression()).size()) > 1) {
return plan;
}
// exprs will be used as filterExprs to check the index
// For forward scan, the initial value is endExprs and might be changed in different values in variant cases
// For reverse scan, the initial value is initialExprs which is the "old" endExprs
List<AbstractExpression> exprs;
int numOfSearchKeys = ispn.getSearchKeyExpressions().size();
if (ispn.getLookupType() == IndexLookupType.LT || ispn.getLookupType() == IndexLookupType.LTE) {
exprs = ExpressionUtil.uncombine(ispn.getInitialExpression());
numOfSearchKeys -= 1;
} else {
exprs = ExpressionUtil.uncombine(ispn.getEndExpression());
}
int numberOfExprs = exprs.size();
// If there is only 1 difference between searchkeyExprs and endExprs,
// 1. trivial filters can be discarded, 2 possibilities:
// a. SELECT MIN(X) FROM T WHERE [other prefix filters] X < / <= ?
// <=> SELECT MIN(X) FROM T WHERE [other prefix filters] && the X < / <= ? filter
// b. SELECT MAX(X) FROM T WHERE X > / >= ?
// <=> SELECT MAX(X) FROM T with post-filter
// 2. filter should act as equality filter, 2 possibilities
// SELECT MIN(X) FROM T WHERE [other prefix filters] X > / >= ?
// SELECT MAX(X) FROM T WHERE [other prefix filters] X < / <= ?
// check if there is other filters for SELECT MAX(X) FROM T WHERE [other prefix filter AND ] X > / >= ?
// but we should allow SELECT MAX(X) FROM T WHERE X = ?
if (sortDirection == SortDirectionType.DESC && ispn.getSortDirection() == SortDirectionType.INVALID) {
if (numberOfExprs > 1 ||
(numberOfExprs == 1 && aggExpr.bindingToIndexedExpression(exprs.get(0).getLeft()) == null)) {
return plan;
}
}
// have an upper bound: # of endingExpr is more than # of searchExpr
if (numberOfExprs > numOfSearchKeys) {
// check last ending condition, see whether it is
// SELECT MIN(X) FROM T WHERE [other prefix filters] X < / <= ? or
// other filters will be checked later
AbstractExpression lastEndExpr = exprs.get(numberOfExprs - 1);
if ((lastEndExpr.getExpressionType() == ExpressionType.COMPARE_LESSTHAN ||
lastEndExpr.getExpressionType() == ExpressionType.COMPARE_LESSTHANOREQUALTO)
&& lastEndExpr.getLeft().equals(aggExpr)) {
exprs.remove(lastEndExpr);
}
}
// do not aggressively evaluate all indexes, just examine the index currently in use;
// because for all qualified indexes, one access plan must have been generated already,
// and we can take advantage of that
if (!checkIndex(ispn.getCatalogIndex(), aggExpr, exprs, ispn.getBindings(), ispn.getTargetTableAlias())) {
return plan;
} else {
// we know which end we want to fetch, set the sort direction
ispn.setSortDirection(sortDirection);
// for SELECT MIN(X) FROM T WHERE [prefix filters] = ?
if (numberOfExprs == numOfSearchKeys && sortDirection == SortDirectionType.ASC) {
if (ispn.getLookupType() == IndexLookupType.GTE) {
assert(aggplan.isTableMin());
ispn.setSkipNullPredicate(numOfSearchKeys);
}
}
// for SELECT MIN(X) FROM T WHERE [...] X < / <= ?
// reset the IndexLookupType, remove "added" searchKey, add back to endExpression, and clear "added" predicate
if (sortDirection == SortDirectionType.ASC &&
(ispn.getLookupType() == IndexLookupType.LT || ispn.getLookupType() == IndexLookupType.LTE)){
ispn.setLookupType(IndexLookupType.GTE);
ispn.removeLastSearchKey();
ispn.addEndExpression(ExpressionUtil.uncombine(ispn.getInitialExpression()).get(numberOfExprs - 1));
ispn.resetPredicate();
}
// add an inline LIMIT plan node to this index scan plan node
LimitPlanNode lpn = new LimitPlanNode();
lpn.setLimit(1);
lpn.setOffset(0);
ispn.addInlinePlanNode(lpn);
// ENG-1565: For SELECT MAX(X) FROM T WHERE X > / >= ?, turn the pre-filter to post filter.
// There are two choices:
// AggregatePlanNode AggregatePlanNode