in exec/java-exec/src/main/java/org/apache/drill/exec/planner/index/IndexSelector.java [221:362]
public void getCandidateIndexes(IndexConditionInfo.Builder infoBuilder, List<IndexGroup> coveringIndexes,
List<IndexGroup> nonCoveringIndexes, List<IndexGroup> intersectIndexes) {
RelOptPlanner planner = indexContext.getCall().getPlanner();
PlannerSettings settings = PrelUtil.getPlannerSettings(planner);
List<IndexGroup> candidateIndexes = Lists.newArrayList();
logger.info("index_plan_info: Analyzing {} indexes for prefix matches: {}",
indexPropList.size(), indexPropList);
// analysis phase
for (IndexProperties p : indexPropList) {
analyzePrefixMatches(p);
// only consider indexes that either have some leading prefix of the filter condition or
// can satisfy required collation
if (p.numLeadingFilters() > 0 || p.satisfiesCollation()) {
double selThreshold = p.isCovering() ? settings.getIndexCoveringSelThreshold() :
settings.getIndexNonCoveringSelThreshold();
// only consider indexes whose selectivity is <= the configured threshold OR consider
// all when full table scan is disable to avoid a CannotPlanException
if (settings.isDisableFullTableScan() || p.getLeadingSelectivity() <= selThreshold) {
IndexGroup index = new IndexGroup();
index.addIndexProp(p);
candidateIndexes.add(index);
}
else {
if (p.getLeadingSelectivity() > selThreshold) {
logger.debug("Skipping index {}. The leading selectivity {} is larger than threshold {}",
p.getIndexDesc().getIndexName(), p.getLeadingSelectivity(), selThreshold);
}
}
}
}
if (candidateIndexes.size() == 0) {
logger.info("index_plan_info: No suitable indexes found !");
return;
}
int max_candidate_indexes = (int)PrelUtil.getPlannerSettings(planner).getIndexMaxChosenIndexesPerTable();
// Ranking phase. Technically, we don't need to rank if there are fewer than max_candidate_indexes
// but we do it anyways for couple of reasons: the log output will show the indexes in a properly ranked
// order which helps diagnosing problems and secondly for internal unit/functional testing we want this code
// to be exercised even for few indexes
if (candidateIndexes.size() > 1) {
Collections.sort(candidateIndexes, new IndexComparator(planner, builder));
}
// Generate index intersections for ranking
addIndexIntersections(candidateIndexes, infoBuilder, settings.getMaxIndexesToIntersect());
// Sort again after intersect plan is added to the list
if (candidateIndexes.size() > 1) {
Collections.sort(candidateIndexes, new IndexComparator(planner, builder));
}
logger.info("index_plan_info: The top ranked indexes are: ");
int count = 0;
boolean foundCovering = false;
boolean foundCoveringCollation = false;
boolean foundNonCoveringCollation = false;
// pick the best N indexes
for (int i=0; i < candidateIndexes.size(); i++) {
IndexGroup index = candidateIndexes.get(i);
if (index.numIndexes() == 1
&& index.getIndexProps().get(0).isCovering()) {
IndexProperties indexProps = index.getIndexProps().get(0);
if (foundCoveringCollation) {
// if previously we already found a higher ranked covering index that satisfies collation,
// then skip this one (note that selectivity and cost considerations were already handled
// by the ranking phase)
logger.debug("index_plan_info: Skipping covering index {} because a higher ranked covering index with collation already exists.", indexProps.getIndexDesc().getIndexName());
continue;
}
coveringIndexes.add(index);
logger.info("index_plan_info: name: {}, covering, collation: {}, leadingSelectivity: {}, cost: {}",
indexProps.getIndexDesc().getIndexName(),
indexProps.satisfiesCollation(),
indexProps.getLeadingSelectivity(),
indexProps.getSelfCost(planner));
count++;
foundCovering = true;
if (indexProps.satisfiesCollation()) {
foundCoveringCollation = true;
}
} else if (index.numIndexes() == 1) { // non-covering
IndexProperties indexProps = index.getIndexProps().get(0);
// skip this non-covering index if (a) there was a higher ranked covering index
// with collation or (b) there was a higher ranked covering index and this
// non-covering index does not have collation
if (foundCoveringCollation ||
(foundCovering && !indexProps.satisfiesCollation())) {
logger.debug("index_plan_info: Skipping non-covering index {} because it does not have collation and a higher ranked covering index already exists.",
indexProps.getIndexDesc().getIndexName());
continue;
}
if (indexProps.satisfiesCollation()) {
foundNonCoveringCollation = true;
}
// all other non-covering indexes can be added to the list because 2 or more non-covering index could
// be considered for intersection later; currently the index selector is not costing the index intersection
nonCoveringIndexes.add(index);
logger.info("index_plan_info: name: {}, non-covering, collation: {}, leadingSelectivity: {}, cost: {}",
indexProps.getIndexDesc().getIndexName(),
indexProps.satisfiesCollation(),
indexProps.getLeadingSelectivity(),
indexProps.getSelfCost(planner));
count++;
} else { // intersect indexes
if (foundCoveringCollation ||
(foundCovering && !index.getIndexProps().get(index.numIndexes()-1).satisfiesCollation()) ||
foundNonCoveringCollation) {
continue;
}
IndexGroup intersectIndex = new IndexGroup();
double isectLeadingSel = 1.0;
String isectName = "Intersect-"+count;
for (IndexProperties indexProps : index.getIndexProps()) {
intersectIndex.addIndexProp(indexProps);
isectLeadingSel *= indexProps.getLeadingSelectivity();
logger.info("name: {}, {}, collation: {}, leadingSelectivity: {}, cost: {}",
indexProps.getIndexDesc().getIndexName(),
isectName,
indexProps.satisfiesCollation(),
indexProps.getLeadingSelectivity(),
indexProps.getSelfCost(planner));
}
logger.info("name: {}, intersect-idx, collation: {}, leadingSelectivity: {}, cost: {}",
isectName,
index.getIndexProps().get(index.numIndexes()-1).satisfiesCollation(),
isectLeadingSel,
index.getIndexProps().get(0).getIntersectCost(index, builder, planner));
intersectIndexes.add(intersectIndex);
}
if (count == max_candidate_indexes) {
break;
}
}
}