org.h2.index.IndexCondition Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of h2-mvstore Show documentation
Show all versions of h2-mvstore Show documentation
Fork of h2database to maintain Java 8 compatibility
The newest version!
/*
* Copyright 2004-2023 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;
import org.h2.command.query.Query;
import org.h2.engine.SessionLocal;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.condition.Comparison;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.table.Column;
import org.h2.table.TableType;
import org.h2.value.Value;
import org.h2.value.ValueArray;
/**
* A index condition object is made for each condition that can potentially use
* an index. This class does not extend expression, but in general there is one
* expression that maps to each index condition.
*
* @author Thomas Mueller
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public class IndexCondition {
/**
* A bit of a search mask meaning 'equal'.
*/
public static final int EQUALITY = 1;
/**
* A bit of a search mask meaning 'larger or equal'.
*/
public static final int START = 2;
/**
* A bit of a search mask meaning 'smaller or equal'.
*/
public static final int END = 4;
/**
* A search mask meaning 'between'.
*/
public static final int RANGE = START | END;
/**
* A bit of a search mask meaning 'the condition is always false'.
*/
public static final int ALWAYS_FALSE = 8;
/**
* A bit of a search mask meaning 'spatial intersection'.
*/
public static final int SPATIAL_INTERSECTS = 16;
private final Column column;
/**
* see constants in {@link Comparison}
*/
private final int compareType;
private final Expression expression;
private List expressionList;
private Query expressionQuery;
/**
* @param compareType the comparison type, see constants in
* {@link Comparison}
*/
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, see constants in
* {@link Comparison}
* @param column the column
* @param expression the expression
* @return the index condition
*/
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,
List list) {
IndexCondition cond = new IndexCondition(Comparison.IN_LIST, column,
null);
cond.expressionList = list;
return cond;
}
/**
* Create an index condition with the compare type IN_ARRAY and with the
* given parameters.
*
* @param column the column
* @param array the array
* @return the index condition
*/
public static IndexCondition getInArray(ExpressionColumn column, Expression array) {
return new IndexCondition(Comparison.IN_ARRAY, column, array);
}
/**
* 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) {
assert query.isRandomAccessResult();
IndexCondition cond = new IndexCondition(Comparison.IN_QUERY, column, null);
cond.expressionQuery = query;
return cond;
}
/**
* Get the current value of the expression.
*
* @param session the session
* @return the value
*/
public Value getCurrentValue(SessionLocal session) {
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(SessionLocal session) {
TreeSet valueSet = new TreeSet<>(session.getDatabase().getCompareMode());
if (compareType == Comparison.IN_LIST) {
for (Expression e : expressionList) {
Value v = e.getValue(session);
v = column.convert(session, v);
valueSet.add(v);
}
} else if (compareType == Comparison.IN_ARRAY) {
Value v = expression.getValue(session);
if (v instanceof ValueArray) {
for (Value e : ((ValueArray) v).getList()) {
valueSet.add(e);
}
}
} else {
throw DbException.getInternalError("compareType = " + compareType);
}
Value[] array = valueSet.toArray(new Value[valueSet.size()]);
Arrays.sort(array, session.getDatabase().getCompareMode());
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.
*
* @return the result
*/
public ResultInterface getCurrentResult() {
return expressionQuery.query(0);
}
/**
* Get the SQL snippet of this comparison.
*
* @param sqlFlags formatting flags
* @return the SQL snippet
*/
public String getSQL(int sqlFlags) {
if (compareType == Comparison.FALSE) {
return "FALSE";
}
StringBuilder builder = new StringBuilder();
column.getSQL(builder, sqlFlags);
switch (compareType) {
case Comparison.EQUAL:
builder.append(" = ");
break;
case Comparison.EQUAL_NULL_SAFE:
builder.append(expression.isNullConstant()
|| column.getType().getValueType() == Value.BOOLEAN && expression.isConstant() //
? " IS "
: " IS NOT DISTINCT FROM ");
break;
case Comparison.BIGGER_EQUAL:
builder.append(" >= ");
break;
case Comparison.BIGGER:
builder.append(" > ");
break;
case Comparison.SMALLER_EQUAL:
builder.append(" <= ");
break;
case Comparison.SMALLER:
builder.append(" < ");
break;
case Comparison.IN_LIST:
Expression.writeExpressions(builder.append(" IN("), expressionList, sqlFlags).append(')');
break;
case Comparison.IN_ARRAY:
return expression.getSQL(builder.append(" = ANY("), sqlFlags, Expression.AUTO_PARENTHESES).append(')')
.toString();
case Comparison.IN_QUERY:
builder.append(" IN(");
builder.append(expressionQuery.getPlanSQL(sqlFlags));
builder.append(')');
break;
case Comparison.SPATIAL_INTERSECTS:
builder.append(" && ");
break;
default:
throw DbException.getInternalError("type=" + compareType);
}
if (expression != null) {
expression.getSQL(builder, sqlFlags, Expression.AUTO_PARENTHESES);
}
return builder.toString();
}
/**
* Get the comparison bit mask.
*
* @param indexConditions all index conditions
* @return the mask
*/
public int getMask(ArrayList indexConditions) {
switch (compareType) {
case Comparison.FALSE:
return ALWAYS_FALSE;
case Comparison.EQUAL:
case Comparison.EQUAL_NULL_SAFE:
return EQUALITY;
case Comparison.IN_LIST:
case Comparison.IN_ARRAY:
case Comparison.IN_QUERY:
if (indexConditions.size() > 1) {
if (TableType.TABLE != column.getTable().getTableType()) {
// if combined with other conditions,
// IN(..) can only be used for regular tables
// test case:
// create table test(a int, b int, primary key(id, name));
// create unique index c on test(b, a);
// insert into test values(1, 10), (2, 20);
// select * from (select * from test)
// where a=1 and b in(10, 20);
return 0;
}
}
return EQUALITY;
case Comparison.BIGGER_EQUAL:
case Comparison.BIGGER:
return START;
case Comparison.SMALLER_EQUAL:
case Comparison.SMALLER:
return END;
case Comparison.SPATIAL_INTERSECTS:
return SPATIAL_INTERSECTS;
default:
throw DbException.getInternalError("type=" + compareType);
}
}
/**
* Check if the result is always false.
*
* @return true if the result will always be false
*/
public boolean isAlwaysFalse() {
return compareType == Comparison.FALSE;
}
/**
* Check if this index condition is of the type column larger or equal to
* value.
*
* @return true if this is a start condition
*/
public boolean isStart() {
switch (compareType) {
case Comparison.EQUAL:
case Comparison.EQUAL_NULL_SAFE:
case Comparison.BIGGER_EQUAL:
case Comparison.BIGGER:
return true;
default:
return false;
}
}
/**
* Check if this index condition is of the type column smaller or equal to
* value.
*
* @return true if this is a end condition
*/
public boolean isEnd() {
switch (compareType) {
case Comparison.EQUAL:
case Comparison.EQUAL_NULL_SAFE:
case Comparison.SMALLER_EQUAL:
case Comparison.SMALLER:
return true;
default:
return false;
}
}
/**
* Check if this index condition is of the type spatial column intersects
* value.
*
* @return true if this is a spatial intersects condition
*/
public boolean isSpatialIntersects() {
switch (compareType) {
case Comparison.SPATIAL_INTERSECTS:
return true;
default:
return false;
}
}
public int getCompareType() {
return compareType;
}
/**
* Get the referenced column.
*
* @return the column
*/
public Column getColumn() {
return column;
}
/**
* Get expression.
*
* @return Expression.
*/
public Expression getExpression() {
return expression;
}
/**
* Get expression list.
*
* @return Expression list.
*/
public List getExpressionList() {
return expressionList;
}
/**
* Get expression query.
*
* @return Expression query.
*/
public Query getExpressionQuery() {
return expressionQuery;
}
/**
* Check if the expression can be evaluated.
*
* @return true if it can be evaluated
*/
public boolean isEvaluatable() {
if (expression != null) {
return expression
.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR);
}
if (expressionList != null) {
for (Expression e : expressionList) {
if (!e.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR)) {
return false;
}
}
return true;
}
return expressionQuery
.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("column=").append(column).append(", compareType=");
return compareTypeToString(builder, compareType)
.append(", expression=").append(expression)
.append(", expressionList=").append(expressionList)
.append(", expressionQuery=").append(expressionQuery).toString();
}
private static StringBuilder compareTypeToString(StringBuilder builder, int i) {
boolean f = false;
if ((i & EQUALITY) == EQUALITY) {
f = true;
builder.append("EQUALITY");
}
if ((i & START) == START) {
if (f) {
builder.append(", ");
}
f = true;
builder.append("START");
}
if ((i & END) == END) {
if (f) {
builder.append(", ");
}
f = true;
builder.append("END");
}
if ((i & ALWAYS_FALSE) == ALWAYS_FALSE) {
if (f) {
builder.append(", ");
}
f = true;
builder.append("ALWAYS_FALSE");
}
if ((i & SPATIAL_INTERSECTS) == SPATIAL_INTERSECTS) {
if (f) {
builder.append(", ");
}
builder.append("SPATIAL_INTERSECTS");
}
return builder;
}
}