package org.h2.index;
import java.util.ArrayList;
import org.h2.engine.Session;
import org.h2.expression.condition.Comparison;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueNull;
public class IndexCursor implements Cursor {
private final TableFilter tableFilter;
private Index index;
private Table table;
private IndexColumn[] indexColumns;
private boolean alwaysFalse;
private SearchRow start, end, intersects;
private Cursor cursor;
private Column inColumn;
private int inListIndex;
private Value[] inList;
private ResultInterface inResult;
public IndexCursor(TableFilter filter) {
this.tableFilter = filter;
}
public void setIndex(Index index) {
this.index = index;
this.table = index.getTable();
Column[] columns = table.getColumns();
indexColumns = new IndexColumn[columns.length];
IndexColumn[] idxCols = index.getIndexColumns();
if (idxCols != null) {
for (int i = 0, len = columns.length; i < len; i++) {
int idx = index.getColumnIndex(columns[i]);
if (idx >= 0) {
indexColumns[i] = idxCols[idx];
}
}
}
}
public void prepare(Session s, ArrayList<IndexCondition> indexConditions) {
alwaysFalse = false;
start = end = null;
inList = null;
inColumn = null;
inResult = null;
intersects = null;
for (IndexCondition condition : indexConditions) {
if (condition.isAlwaysFalse()) {
alwaysFalse = true;
break;
}
if (index.isFindUsingFullTableScan()) {
continue;
}
Column column = condition.getColumn();
if (condition.getCompareType() == Comparison.IN_LIST) {
if (start == null && end == null) {
if (canUseIndexForIn(column)) {
this.inColumn = column;
inList = condition.getCurrentValueList(s);
inListIndex = 0;
}
}
} else if (condition.getCompareType() == Comparison.IN_QUERY) {
if (start == null && end == null) {
if (canUseIndexForIn(column)) {
this.inColumn = column;
inResult = condition.getCurrentResult();
}
}
} else {
Value v = condition.getCurrentValue(s);
boolean isStart = condition.isStart();
boolean isEnd = condition.isEnd();
boolean isIntersects = condition.isSpatialIntersects();
int columnId = column.getColumnId();
if (columnId != SearchRow.ROWID_INDEX) {
IndexColumn idxCol = indexColumns[columnId];
if (idxCol != null && (idxCol.sortType & SortOrder.DESCENDING) != 0) {
boolean temp = isStart;
isStart = isEnd;
isEnd = temp;
}
}
if (isStart) {
start = getSearchRow(start, columnId, v, true);
}
if (isEnd) {
end = getSearchRow(end, columnId, v, false);
}
if (isIntersects) {
intersects = getSpatialSearchRow(intersects, columnId, v);
}
if ((isStart || isEnd) && !canUseIndexFor(inColumn)) {
inColumn = null;
inList = null;
inResult = null;
}
}
}
if (inColumn != null) {
start = table.getTemplateRow();
}
}
public void find(Session s, ArrayList<IndexCondition> indexConditions) {
prepare(s, indexConditions);
if (inColumn != null) {
return;
}
if (!alwaysFalse) {
if (intersects != null && index instanceof SpatialIndex) {
cursor = ((SpatialIndex) index).findByGeometry(tableFilter,
start, end, intersects);
} else if (index != null) {
cursor = index.find(tableFilter, start, end);
}
}
}
private boolean canUseIndexForIn(Column column) {
if (inColumn != null) {
return false;
}
return canUseIndexFor(column);
}
private boolean canUseIndexFor(Column column) {
IndexColumn[] cols = index.getIndexColumns();
if (cols == null) {
return true;
}
IndexColumn idxCol = cols[0];
return idxCol == null || idxCol.column == column;
}
private SearchRow getSpatialSearchRow(SearchRow row, int columnId, Value v) {
if (row == null) {
row = table.getTemplateRow();
} else if (row.getValue(columnId) != null) {
ValueGeometry vg = (ValueGeometry) row.getValue(columnId).
convertTo(Value.GEOMETRY);
v = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).
getEnvelopeUnion(vg);
}
if (columnId == SearchRow.ROWID_INDEX) {
row.setKey(v.getLong());
} else {
row.setValue(columnId, v);
}
return row;
}
private SearchRow getSearchRow(SearchRow row, int columnId, Value v, boolean max) {
if (row == null) {
row = table.getTemplateRow();
} else {
v = getMax(row.getValue(columnId), v, max);
}
if (columnId == SearchRow.ROWID_INDEX) {
row.setKey(v.getLong());
} else {
row.setValue(columnId, v);
}
return row;
}
private Value getMax(Value a, Value b, boolean bigger) {
if (a == null) {
return b;
} else if (b == null) {
return a;
}
if (a == ValueNull.INSTANCE) {
return b;
} else if (b == ValueNull.INSTANCE) {
return a;
}
int comp = table.getDatabase().compare(a, b);
if (comp == 0) {
return a;
}
return (comp > 0) == bigger ? a : b;
}
public boolean isAlwaysFalse() {
return alwaysFalse;
}
public SearchRow getStart() {
return start;
}
public SearchRow getEnd() {
return end;
}
@Override
public Row get() {
if (cursor == null) {
return null;
}
return cursor.get();
}
@Override
public SearchRow getSearchRow() {
return cursor.getSearchRow();
}
@Override
public boolean next() {
while (true) {
if (cursor == null) {
nextCursor();
if (cursor == null) {
return false;
}
}
if (cursor.next()) {
return true;
}
cursor = null;
}
}
private void nextCursor() {
if (inList != null) {
while (inListIndex < inList.length) {
Value v = inList[inListIndex++];
if (v != ValueNull.INSTANCE) {
find(v);
break;
}
}
} else if (inResult != null) {
while (inResult.next()) {
Value v = inResult.currentRow()[0];
if (v != ValueNull.INSTANCE) {
find(v);
break;
}
}
}
}
private void find(Value v) {
v = inColumn.convert(v);
int id = inColumn.getColumnId();
start.setValue(id, v);
cursor = index.find(tableFilter, start, start);
}
@Override
public boolean previous() {
throw DbException.throwInternalError(toString());
}
}