提交 18a3b3c2 authored 作者: Thomas Mueller's avatar Thomas Mueller

An optimization for IN(..) and IN(SELECT...) using the system property h2.optimizeInList

上级 284e2b74
......@@ -1715,7 +1715,7 @@ public class Parser {
read("NULL");
r = new Comparison(session, type, r, null);
} else if (readIf("IN")) {
if (SysProperties.OPTIMIZE_IN) {
if (SysProperties.OPTIMIZE_IN && !SysProperties.OPTIMIZE_IN_LIST) {
recompileAlways = true;
}
read("(");
......
......@@ -437,6 +437,14 @@ public class SysProperties {
*/
public static final boolean OPTIMIZE_IN = getBooleanSetting("h2.optimizeIn", true);
/**
* System property <code>h2.optimizeInList</code> (default: false).<br />
* Optimize IN(...) and IN(SELECT ...) comparisons. This includes
* optimization for SELECT, DELETE, and UPDATE. Overrides h2.optimizeIn and
* h2.optimizeInJoin if enabled.
*/
public static final boolean OPTIMIZE_IN_LIST = getBooleanSetting("h2.optimizeInList", false);
/**
* System property <code>h2.optimizeInJoin</code> (default: false).<br />
* Optimize IN(...) comparisons by converting them to inner joins.
......
......@@ -174,14 +174,14 @@ public class CompareLike extends Condition {
}
String begin = buff.toString();
if (maxMatch == patternLength) {
filter.addIndexCondition(new IndexCondition(Comparison.EQUAL, l, ValueExpression
filter.addIndexCondition(IndexCondition.get(Comparison.EQUAL, l, ValueExpression
.get(ValueString.get(begin))));
} else {
// TODO check if this is correct according to Unicode rules (code
// points)
String end;
if (begin.length() > 0) {
filter.addIndexCondition(new IndexCondition(Comparison.BIGGER_EQUAL, l, ValueExpression.get(ValueString
filter.addIndexCondition(IndexCondition.get(Comparison.BIGGER_EQUAL, l, ValueExpression.get(ValueString
.get(begin))));
char next = begin.charAt(begin.length() - 1);
// search the 'next' unicode character (or at least a character
......@@ -189,7 +189,7 @@ public class CompareLike extends Condition {
for (int i = 1; i < 2000; i++) {
end = begin.substring(0, begin.length() - 1) + (char) (next + i);
if (compareMode.compareString(begin, end, ignoreCase) == -1) {
filter.addIndexCondition(new IndexCondition(Comparison.SMALLER, l, ValueExpression
filter.addIndexCondition(IndexCondition.get(Comparison.SMALLER, l, ValueExpression
.get(ValueString.get(end))));
break;
}
......
......@@ -71,6 +71,18 @@ public class Comparison extends Condition {
*/
public static final int FALSE = 8;
/**
* This is a pseudo comparison type that is only used for index conditions.
* It means equals any value of a list. Example: IN(1, 2, 3).
*/
public static final int IN_LIST = 9;
/**
* This is a pseudo comparison type that is only used for index conditions.
* It means equals any value of a list. Example: IN(SELECT ...).
*/
public static final int IN_QUERY = 10;
private final Database database;
private final int compareType;
private Expression left;
......@@ -366,10 +378,10 @@ public class Comparison extends Condition {
}
if (addIndex) {
if (l != null) {
filter.addIndexCondition(new IndexCondition(compareType, l, right));
filter.addIndexCondition(IndexCondition.get(compareType, l, right));
} else if (r != null) {
int compareRev = getReversedCompareType(compareType);
filter.addIndexCondition(new IndexCondition(compareRev, r, left));
filter.addIndexCondition(IndexCondition.get(compareRev, r, left));
}
}
return;
......
......@@ -7,7 +7,6 @@
package org.h2.expression;
import java.sql.SQLException;
import org.h2.command.dml.Select;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
......@@ -34,19 +33,19 @@ public class ConditionIn extends Condition {
private final Database database;
private Expression left;
private final ObjectArray<Expression> values;
private final ObjectArray<Expression> valueList;
private Value min, max;
private int queryLevel;
public ConditionIn(Database database, Expression left, ObjectArray<Expression> values) {
this.database = database;
this.left = left;
this.values = values;
this.valueList = values;
}
public Value getValue(Session session) throws SQLException {
Value l = left.getValue(session);
if (values.size() == 0) {
if (valueList.size() == 0) {
return ValueBoolean.get(false);
}
if (l == ValueNull.INSTANCE) {
......@@ -54,11 +53,12 @@ public class ConditionIn extends Condition {
}
boolean result = false;
boolean hasNull = false;
for (Expression e : values) {
for (Expression e : valueList) {
Value r = e.getValue(session);
if (r == ValueNull.INSTANCE) {
hasNull = true;
} else {
r = r.convertTo(l.getType());
result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL);
if (result) {
break;
......@@ -73,14 +73,14 @@ public class ConditionIn extends Condition {
public void mapColumns(ColumnResolver resolver, int queryLevel) throws SQLException {
left.mapColumns(resolver, queryLevel);
for (Expression e : values) {
for (Expression e : valueList) {
e.mapColumns(resolver, queryLevel);
}
this.queryLevel = Math.max(queryLevel, this.queryLevel);
}
public Expression optimize(Session session) throws SQLException {
if (values.size() == 0) {
if (valueList.size() == 0) {
return ValueExpression.get(ValueBoolean.get(false));
}
left = left.optimize(session);
......@@ -89,25 +89,24 @@ public class ConditionIn extends Condition {
return left;
}
boolean allValuesConstant = true;
for (int i = 0; i < values.size(); i++) {
Expression e = values.get(i);
for (int i = 0; i < valueList.size(); i++) {
Expression e = valueList.get(i);
e = e.optimize(session);
if (allValuesConstant && !e.isConstant()) {
allValuesConstant = false;
}
values.set(i, e);
valueList.set(i, e);
}
if (constant && allValuesConstant) {
return ValueExpression.get(getValue(session));
}
// TODO optimization: could use index in some cases (sort, use min and max)
if (values.size() == 1) {
Expression right = values.get(0);
if (valueList.size() == 1) {
Expression right = valueList.get(0);
Expression expr = new Comparison(session, Comparison.EQUAL, left, right);
expr = expr.optimize(session);
return expr;
}
if (SysProperties.OPTIMIZE_IN) {
if (SysProperties.OPTIMIZE_IN && !SysProperties.OPTIMIZE_IN_LIST) {
int dataType = left.getType();
ExpressionVisitor independent = ExpressionVisitor.get(ExpressionVisitor.INDEPENDENT);
independent.setQueryLevel(queryLevel);
......@@ -116,11 +115,11 @@ public class ConditionIn extends Condition {
Column column = ((ExpressionColumn) left).getColumn();
boolean nullable = column.isNullable();
CompareMode mode = session.getDatabase().getCompareMode();
for (int i = 0; i < values.size(); i++) {
Expression e = values.get(i);
for (int i = 0; i < valueList.size(); i++) {
Expression e = valueList.get(i);
Value v = e.getValue(session);
v = v.convertTo(dataType);
values.set(i, ValueExpression.get(v));
valueList.set(i, ValueExpression.get(v));
if (min == null || min.compareTo(v, mode) > 0) {
if (v != ValueNull.INSTANCE || nullable) {
min = v;
......@@ -137,26 +136,36 @@ public class ConditionIn extends Condition {
}
public void createIndexConditions(Session session, TableFilter filter) {
if (!SysProperties.OPTIMIZE_IN) {
if (!(left instanceof ExpressionColumn)) {
return;
}
if (min == null && max == null) {
ExpressionColumn l = (ExpressionColumn) left;
if (filter != l.getTableFilter()) {
return;
}
if (!(left instanceof ExpressionColumn)) {
if (SysProperties.OPTIMIZE_IN_LIST) {
ExpressionVisitor visitor = ExpressionVisitor.get(ExpressionVisitor.NOT_FROM_RESOLVER);
visitor.setResolver(filter);
for (Expression e : valueList) {
if (!e.isEverything(visitor)) {
return;
}
ExpressionColumn l = (ExpressionColumn) left;
if (filter != l.getTableFilter()) {
}
filter.addIndexCondition(IndexCondition.getInList(l, valueList));
}
if (!SysProperties.OPTIMIZE_IN) {
return;
}
filter.addIndexCondition(new IndexCondition(Comparison.BIGGER_EQUAL, l, ValueExpression.get(min)));
filter.addIndexCondition(new IndexCondition(Comparison.SMALLER_EQUAL, l, ValueExpression.get(max)));
if (min == null && max == null) {
return;
}
filter.addIndexCondition(IndexCondition.get(Comparison.BIGGER_EQUAL, l, ValueExpression.get(min)));
filter.addIndexCondition(IndexCondition.get(Comparison.SMALLER_EQUAL, l, ValueExpression.get(max)));
}
public void setEvaluatable(TableFilter tableFilter, boolean b) {
left.setEvaluatable(tableFilter, b);
for (Expression e : values) {
for (Expression e : valueList) {
e.setEvaluatable(tableFilter, b);
}
}
......@@ -164,7 +173,7 @@ public class ConditionIn extends Condition {
public String getSQL() {
StatementBuilder buff = new StatementBuilder("(");
buff.append(left.getSQL()).append(" IN(");
for (Expression e : values) {
for (Expression e : valueList) {
buff.appendExceptFirst(", ");
buff.append(e.getSQL());
}
......@@ -173,7 +182,7 @@ public class ConditionIn extends Condition {
public void updateAggregate(Session session) throws SQLException {
left.updateAggregate(session);
for (Expression e : values) {
for (Expression e : valueList) {
e.updateAggregate(session);
}
}
......@@ -186,7 +195,7 @@ public class ConditionIn extends Condition {
}
private boolean areAllValues(ExpressionVisitor visitor) {
for (Expression e : values) {
for (Expression e : valueList) {
if (!e.isEverything(visitor)) {
return false;
}
......@@ -196,13 +205,16 @@ public class ConditionIn extends Condition {
public int getCost() {
int cost = left.getCost();
for (Expression e : values) {
for (Expression e : valueList) {
cost += e.getCost();
}
return cost;
}
public Expression optimizeInJoin(Session session, Select select) throws SQLException {
if (SysProperties.OPTIMIZE_IN_LIST) {
return this;
}
if (!areAllValues(ExpressionVisitor.get(ExpressionVisitor.EVALUATABLE))) {
return this;
}
......@@ -219,11 +231,11 @@ public class ConditionIn extends Condition {
}
Database db = session.getDatabase();
Schema mainSchema = db.getSchema(Constants.SCHEMA_MAIN);
int rowCount = values.size();
int rowCount = valueList.size();
TableFunction function = new TableFunction(database, Function.getFunctionInfo("TABLE_DISTINCT"), rowCount);
Expression[] array = new Expression[rowCount];
for (int i = 0; i < rowCount; i++) {
Expression e = values.get(i);
Expression e = valueList.get(i);
array[i] = e;
}
ExpressionList list = new ExpressionList(array);
......
......@@ -11,9 +11,11 @@ import java.sql.SQLException;
import org.h2.command.dml.Query;
import org.h2.command.dml.Select;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.index.Index;
import org.h2.index.IndexCondition;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.table.ColumnResolver;
......@@ -94,8 +96,7 @@ public class ConditionInSelect extends Condition {
if (query.getColumnCount() != 1) {
throw Message.getSQLException(ErrorCode.SUBQUERY_IS_NOT_SINGLE_COLUMN);
}
// Can not optimize IN(SELECT...): the data may change
// However, could transform to an inner join
// Can not optimize: the data may change
return this;
}
......@@ -125,6 +126,9 @@ public class ConditionInSelect extends Condition {
public Expression optimizeInJoin(Session session, Select select) throws SQLException {
query.setDistinct(true);
if (SysProperties.OPTIMIZE_IN_LIST) {
return this;
}
if (all || compareType != Comparison.EQUAL) {
return this;
}
......@@ -157,4 +161,23 @@ public class ConditionInSelect extends Condition {
return on;
}
public void createIndexConditions(Session session, TableFilter filter) {
if (!SysProperties.OPTIMIZE_IN_LIST) {
return;
}
if (!(left instanceof ExpressionColumn)) {
return;
}
ExpressionColumn l = (ExpressionColumn) left;
if (filter != l.getTableFilter()) {
return;
}
ExpressionVisitor visitor = ExpressionVisitor.get(ExpressionVisitor.NOT_FROM_RESOLVER);
visitor.setResolver(filter);
if (!query.isEverything(visitor)) {
return;
}
filter.addIndexCondition(IndexCondition.getInQuery(l, query));
}
}
......@@ -266,7 +266,7 @@ public class ExpressionColumn extends Expression {
public void createIndexConditions(Session session, TableFilter filter) {
TableFilter tf = getTableFilter();
if (filter == tf && column.getType() == Value.BOOLEAN) {
IndexCondition cond = new IndexCondition(Comparison.EQUAL, this, ValueExpression
IndexCondition cond = IndexCondition.get(Comparison.EQUAL, this, ValueExpression
.get(ValueBoolean.get(true)));
filter.addIndexCondition(cond);
}
......
......@@ -80,7 +80,7 @@ public class ValueExpression extends Expression {
if (value.getType() == Value.BOOLEAN) {
boolean v = ((ValueBoolean) value).getBoolean().booleanValue();
if (!v) {
filter.addIndexCondition(new IndexCondition(Comparison.FALSE, null, this));
filter.addIndexCondition(IndexCondition.get(Comparison.FALSE, null, this));
}
}
}
......
......@@ -7,14 +7,21 @@
package org.h2.index;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import org.h2.command.dml.Query;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.table.Column;
import org.h2.util.ObjectArray;
import org.h2.util.StatementBuilder;
import org.h2.value.CompareMode;
import org.h2.value.Value;
/**
......@@ -50,20 +57,56 @@ public class IndexCondition {
public static final int ALWAYS_FALSE = 8;
private Column column;
private Expression expression;
private int compareType;
private Expression expression;
private ObjectArray<Expression> expressionList;
private Query expressionQuery;
private IndexCondition(int compareType, ExpressionColumn column, Expression expression) {
this.compareType = compareType;
this.column = column == null ? null : column.getColumn();
this.expression = expression;
}
/**
* Create an index condition with the given parameters.
*
* @param compareType the comparison type
* @param column the column
* @param expression the expression
* @return the index condition
*/
public IndexCondition(int compareType, ExpressionColumn column, Expression expression) {
this.compareType = compareType;
this.column = column == null ? null : column.getColumn();
this.expression = expression;
public static IndexCondition get(int compareType, ExpressionColumn column, Expression expression) {
return new IndexCondition(compareType, column, expression);
}
/**
* Create an index condition with the compare type IN_LIST and with the
* given parameters.
*
* @param column the column
* @param list the expression list
* @return the index condition
*/
public static IndexCondition getInList(ExpressionColumn column, ObjectArray<Expression> list) {
IndexCondition cond = new IndexCondition(Comparison.IN_LIST, column, null);
cond.expressionList = list;
return cond;
}
/**
* Create an index condition with the compare type IN_QUERY and with the
* given parameters.
*
* @param column the column
* @param query the select statement
* @return the index condition
*/
public static IndexCondition getInQuery(ExpressionColumn column, Query query) {
IndexCondition cond = new IndexCondition(Comparison.IN_QUERY, column, null);
cond.expressionQuery = query;
return cond;
}
/**
......@@ -76,6 +119,47 @@ public class IndexCondition {
return expression.getValue(session);
}
/**
* Get the current value list of the expression. The value list is of the
* same type as the column, distinct, and sorted.
*
* @param session the session
* @return the value list
*/
public Value[] getCurrentValueList(Session session) throws SQLException {
HashSet<Value> valueSet = new HashSet<Value>();
int dataType = column.getType();
for (Expression e : expressionList) {
Value v = e.getValue(session);
v = v.convertTo(dataType);
valueSet.add(v);
}
Value[] array = new Value[valueSet.size()];
valueSet.toArray(array);
final CompareMode mode = session.getDatabase().getCompareMode();
Arrays.sort(array, new Comparator<Value>() {
public int compare(Value o1, Value o2) {
try {
return o1.compareTo(o2, mode);
} catch (SQLException e) {
throw Message.convertToInternal(e);
}
}
});
return array;
}
/**
* Get the current result of the expression. The rows may not be of the same
* type, therefore the rows may not be unique.
*
* @param session the session
* @return the result
*/
public LocalResult getCurrentResult(Session session) throws SQLException {
return expressionQuery.query(0);
}
/**
* Get the SQL snippet of this comparison.
*
......@@ -85,7 +169,7 @@ public class IndexCondition {
if (compareType == Comparison.FALSE) {
return "FALSE";
}
StringBuilder buff = new StringBuilder();
StatementBuilder buff = new StatementBuilder();
buff.append(column.getSQL());
switch(compareType) {
case Comparison.EQUAL:
......@@ -103,10 +187,25 @@ public class IndexCondition {
case Comparison.SMALLER:
buff.append(" < ");
break;
case Comparison.IN_LIST:
buff.append(" IN(");
for (Expression e : expressionList) {
buff.appendExceptFirst(", ");
buff.append(e.getSQL());
}
buff.append(')');
break;
case Comparison.IN_QUERY:
buff.append(" IN(");
buff.append(expressionQuery.getPlanSQL());
buff.append(')');
break;
default:
Message.throwInternalError("type="+compareType);
}
if (expression != null) {
buff.append(expression.getSQL());
}
return buff.toString();
}
......@@ -120,6 +219,8 @@ public class IndexCondition {
case Comparison.FALSE:
return ALWAYS_FALSE;
case Comparison.EQUAL:
case Comparison.IN_LIST:
case Comparison.IN_QUERY:
return EQUALITY;
case Comparison.BIGGER_EQUAL:
case Comparison.BIGGER:
......@@ -175,6 +276,10 @@ public class IndexCondition {
}
}
public int getCompareType() {
return compareType;
}
/**
* Get the referenced column.
*
......@@ -190,7 +295,18 @@ public class IndexCondition {
* @return true if it can be evaluated
*/
public boolean isEvaluatable() {
if (expression != null) {
return expression.isEverything(ExpressionVisitor.EVALUATABLE);
}
if (expressionList != null) {
for (Expression e : expressionList) {
if (!e.isEverything(ExpressionVisitor.EVALUATABLE)) {
return false;
}
}
return true;
}
return expressionQuery.isEverything(ExpressionVisitor.EVALUATABLE);
}
}
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.sql.SQLException;
import java.util.HashSet;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.message.Message;
import org.h2.result.LocalResult;
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.util.ObjectArray;
import org.h2.value.Value;
/**
* The filter used to walk through an index. This class filters supports IN(..)
* and IN(SELECT ...) optimizations.
*/
public class IndexCursor implements Cursor {
private Session session;
private Index index;
private Table table;
private IndexColumn[] indexColumns;
private boolean alwaysFalse;
private SearchRow start, end;
private Cursor cursor;
private Column inColumn;
private int inListIndex;
private Value[] inList;
private LocalResult inResult;
private HashSet<Value> inResultTested;
public IndexCursor(Session session) {
this.session = session;
}
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; i < columns.length; i++) {
int idx = index.getColumnIndex(columns[i]);
if (idx >= 0) {
indexColumns[i] = idxCols[idx];
}
}
}
}
/**
* Re-evaluate the start and end values of the index search for rows.
*
* @param indexConditions the index conditions
*/
public void find(ObjectArray<IndexCondition> indexConditions) throws SQLException {
alwaysFalse = false;
start = end = null;
inList = null;
inResult = null;
inResultTested = new HashSet<Value>();
for (IndexCondition condition : indexConditions) {
if (condition.isAlwaysFalse()) {
alwaysFalse = true;
break;
}
Column column = condition.getColumn();
int type = column.getType();
int id = column.getColumnId();
if (condition.getCompareType() == Comparison.IN_LIST) {
this.inColumn = column;
inList = condition.getCurrentValueList(session);
inListIndex = 0;
return;
} else if (condition.getCompareType() == Comparison.IN_QUERY) {
this.inColumn = column;
inResult = condition.getCurrentResult(session);
return;
} else {
Value v = condition.getCurrentValue(session).convertTo(type);
boolean isStart = condition.isStart();
boolean isEnd = condition.isEnd();
IndexColumn idxCol = indexColumns[id];
if (idxCol != null && (idxCol.sortType & SortOrder.DESCENDING) != 0) {
// if the index column is sorted the other way, we swap end and start
// NULLS_FIRST / NULLS_LAST is not a problem, as nulls never match anyway
boolean temp = isStart;
isStart = isEnd;
isEnd = temp;
}
if (isStart) {
start = getSearchRow(start, id, v, true);
}
if (isEnd) {
end = getSearchRow(end, id, v, false);
}
}
}
if (!alwaysFalse) {
cursor = index.find(session, start, end);
}
}
private SearchRow getSearchRow(SearchRow row, int id, Value v, boolean max) throws SQLException {
if (row == null) {
row = table.getTemplateRow();
} else {
v = getMax(row.getValue(id), v, max);
}
row.setValue(id, v);
return row;
}
private Value getMax(Value a, Value b, boolean bigger) throws SQLException {
if (a == null) {
return b;
} else if (b == null) {
return a;
}
int comp = a.compareTo(b, table.getDatabase().getCompareMode());
if (!bigger) {
comp = -comp;
}
return comp > 0 ? a : b;
}
/**
* Check if the result is empty for sure.
*
* @return true if it is
*/
public boolean isAlwaysFalse() {
return alwaysFalse;
}
public Row get() throws SQLException {
return cursor.get();
}
public int getPos() {
return cursor.getPos();
}
public SearchRow getSearchRow() throws SQLException {
return cursor.getSearchRow();
}
public boolean next() throws SQLException {
while (true) {
if (cursor == null) {
nextCursor();
if (cursor == null) {
return false;
}
}
if (cursor.next()) {
return true;
}
cursor = null;
}
}
private void nextCursor() throws SQLException {
if (inList != null) {
if (inListIndex < inList.length) {
Value v = inList[inListIndex++];
find(v);
}
} else if (inResult != null) {
while (inResult.next()) {
Value v = inResult.currentRow()[0];
v = v.convertTo(inColumn.getType());
if (inResultTested.add(v)) {
find(v);
break;
}
}
}
}
private void find(Value v) throws SQLException {
v = v.convertTo(inColumn.getType());
int id = inColumn.getColumnId();
if (start == null) {
start = table.getTemplateRow();
}
start.setValue(id, v);
cursor = index.find(session, start, start);
}
public boolean previous() {
throw Message.throwInternalError();
}
}
......@@ -854,6 +854,7 @@ public class MetaTable extends Table {
add(rows, new String[]{"h2.objectCacheMaxPerElementSize", "" + SysProperties.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE});
add(rows, new String[]{"h2.optimizeIn", "" + SysProperties.OPTIMIZE_IN});
add(rows, new String[]{"h2.optimizeInJoin", "" + SysProperties.optimizeInJoin});
add(rows, new String[]{"h2.optimizeInList", "" + SysProperties.OPTIMIZE_IN_LIST});
add(rows, new String[]{"h2.optimizeMinMax", "" + SysProperties.OPTIMIZE_MIN_MAX});
add(rows, new String[]{"h2.optimizeSubqueryCache", "" + SysProperties.OPTIMIZE_SUBQUERY_CACHE});
add(rows, new String[]{"h2.overflowExceptions", "" + SysProperties.OVERFLOW_EXCEPTIONS});
......
......@@ -16,13 +16,12 @@ import org.h2.engine.Session;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexCondition;
import org.h2.index.IndexCursor;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.util.ObjectArray;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
......@@ -40,8 +39,6 @@ public class TableFilter implements ColumnResolver {
private String alias;
private Session session;
private Index index;
private IndexColumn[] indexColumns;
private Cursor cursor;
private int scanCount;
/**
......@@ -49,21 +46,31 @@ public class TableFilter implements ColumnResolver {
*/
private boolean used;
// conditions that can be used for direct index lookup (start or end)
/**
* The filter used to walk through the index.
*/
private final IndexCursor cursor;
/**
* The index conditions used for direct index lookup (start or end).
*/
private final ObjectArray<IndexCondition> indexConditions = ObjectArray.newInstance();
// conditions that can't be used for index lookup,
// but for row filter for this table (ID=ID, NAME LIKE '%X%')
/**
* Additional conditions that can't be used for index lookup,
* but for row filter for this table (ID=ID, NAME LIKE '%X%')
*/
private Expression filterCondition;
// the complete join condition
/**
* The complete join condition.
*/
private Expression joinCondition;
private SearchRow currentSearchRow;
private Row current;
private int state;
private TableFilter join;
private boolean outerJoin;
private ObjectArray<Column> naturalJoinColumns;
private boolean foundOne;
......@@ -85,6 +92,7 @@ public class TableFilter implements ColumnResolver {
this.table = table;
this.alias = alias;
this.select = select;
this.cursor = new IndexCursor(session);
if (!rightsChecked) {
session.getUser().checkRight(table, Right.SELECT);
}
......@@ -229,71 +237,17 @@ public class TableFilter implements ColumnResolver {
foundOne = false;
}
private Value getMax(Value a, Value b, boolean bigger) throws SQLException {
if (a == null) {
return b;
} else if (b == null) {
return a;
}
int comp = a.compareTo(b, session.getDatabase().getCompareMode());
if (!bigger) {
comp = -comp;
}
return comp > 0 ? a : b;
}
/**
* Check if there are more rows to read.
*
* @return true if there are
*/
public boolean next() throws SQLException {
boolean alwaysFalse = false;
if (state == AFTER_LAST) {
return false;
} else if (state == BEFORE_FIRST) {
SearchRow start = null, end = null;
for (IndexCondition condition : indexConditions) {
if (condition.isAlwaysFalse()) {
alwaysFalse = true;
break;
}
Column column = condition.getColumn();
int type = column.getType();
int id = column.getColumnId();
Value v = condition.getCurrentValue(session).convertTo(type);
boolean isStart = condition.isStart(), isEnd = condition.isEnd();
IndexColumn idxCol = indexColumns[id];
if (idxCol != null && (idxCol.sortType & SortOrder.DESCENDING) != 0) {
// if the index column is sorted the other way, we swap end and start
// NULLS_FIRST / NULLS_LAST is not a problem, as nulls never match anyway
boolean temp = isStart;
isStart = isEnd;
isEnd = temp;
}
if (isStart) {
Value newStart;
if (start == null) {
start = table.getTemplateRow();
newStart = v;
} else {
newStart = getMax(start.getValue(id), v, true);
}
start.setValue(id, newStart);
}
if (isEnd) {
Value newEnd;
if (end == null) {
end = table.getTemplateRow();
newEnd = v;
} else {
newEnd = getMax(end.getValue(id), v, false);
}
end.setValue(id, newEnd);
}
}
if (!alwaysFalse) {
cursor = index.find(session, start, end);
cursor.find(indexConditions);
if (!cursor.isAlwaysFalse()) {
if (join != null) {
join.reset();
}
......@@ -310,7 +264,7 @@ public class TableFilter implements ColumnResolver {
if (state == NULL_ROW) {
break;
}
if (alwaysFalse) {
if (cursor.isAlwaysFalse()) {
state = AFTER_LAST;
} else {
if ((++scanCount & 4095) == 0) {
......@@ -319,7 +273,6 @@ public class TableFilter implements ColumnResolver {
if (cursor.next()) {
currentSearchRow = cursor.getSearchRow();
current = null;
// cursor.get();
state = FOUND;
} else {
state = AFTER_LAST;
......@@ -566,17 +519,7 @@ public class TableFilter implements ColumnResolver {
public void setIndex(Index index) {
this.index = index;
Column[] columns = table.getColumns();
indexColumns = new IndexColumn[columns.length];
IndexColumn[] idxCols = index.getIndexColumns();
if (idxCols != null) {
for (int i = 0; i < columns.length; i++) {
int idx = index.getColumnIndex(columns[i]);
if (idx >= 0) {
indexColumns[i] = idxCols[idx];
}
}
}
cursor.setIndex(index);
}
public void setUsed(boolean used) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论