/** * Get the index that matches the ORDER BY list, if one exists. This is to avoid running a * separate ORDER BY if an index can be used. This is specially important for large result sets, * if only the first few rows are important (LIMIT is used) * * @return the index if one is found */ private Index getSortIndex() { if (sort == null) { return null; } ArrayList<Column> sortColumns = New.arrayList(); for (int idx : sort.getIndexes()) { if (idx < 0 || idx >= expressions.size()) { throw DbException.getInvalidValueException("ORDER BY", idx + 1); } Expression expr = expressions.get(idx); expr = expr.getNonAliasExpression(); if (expr.isConstant()) { continue; } if (!(expr instanceof ExpressionColumn)) { return null; } ExpressionColumn exprCol = (ExpressionColumn) expr; if (exprCol.getTableFilter() != topTableFilter) { return null; } sortColumns.add(exprCol.getColumn()); } Column[] sortCols = sortColumns.toArray(new Column[sortColumns.size()]); int[] sortTypes = sort.getSortTypes(); if (sortCols.length == 0) { // sort just on constants - can use scan index return topTableFilter.getTable().getScanIndex(session); } ArrayList<Index> list = topTableFilter.getTable().getIndexes(); if (list != null) { for (int i = 0, size = list.size(); i < size; i++) { Index index = list.get(i); if (index.getCreateSQL() == null) { // can't use the scan index continue; } if (index.getIndexType().isHash()) { continue; } IndexColumn[] indexCols = index.getIndexColumns(); if (indexCols.length < sortCols.length) { continue; } boolean ok = true; for (int j = 0; j < sortCols.length; j++) { // the index and the sort order must start // with the exact same columns IndexColumn idxCol = indexCols[j]; Column sortCol = sortCols[j]; if (idxCol.column != sortCol) { ok = false; break; } if (idxCol.sortType != sortTypes[j]) { // NULL FIRST for ascending and NULLS LAST // for descending would actually match the default ok = false; break; } } if (ok) { return index; } } } if (sortCols.length == 1 && sortCols[0].getColumnId() == -1) { // special case: order by _ROWID_ Index index = topTableFilter.getTable().getScanIndex(session); if (index.isRowIdIndex()) { return index; } } return null; }