use of com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter in project hazelcast by hazelcast.
the class IndexResolver method composeEqualsFilter.
/**
* Composes an equality filter from multiple single-column components.
* <p>
* If the number of single-column filters is equal to the number of index components, the resulting filter is a composite
* equality filter.
* <p>
* If the number of single-column filters is less than the number of index components, the resulting filter is a range
* filter, with missing components filled with negative/positive infinities for the left and right bounds respectively.
* <p>
* If the range filter is required, and the target index type is not {@link IndexType#SORTED}, the result is {@code null}.
* <p>
* Examples:
* <ul>
* <li>SORTED(a, b), {a=1, b=2} => EQUALS(1), EQUALS(2) </li>
* <li>HASH(a, b), {a=1, b=2} => EQUALS(1), EQUALS(2) </li>
* <li>SORTED(a, b), {a=1} => EQUALS(1), RANGE(INF) </li>
* <li>HASH(a, b), {a=1} => null </li>
* </ul>
*
* @return composite filter or {@code null}
*/
private static IndexFilter composeEqualsFilter(List<IndexFilter> filters, IndexEqualsFilter lastFilter, IndexType indexType, int indexComponentsCount) {
// Flatten all known values.
List<Expression> components = new ArrayList<>(filters.size());
List<Boolean> allowNulls = new ArrayList<>(filters.size());
fillNonTerminalComponents(filters, components, allowNulls);
components.addAll(lastFilter.getValue().getComponents());
allowNulls.addAll(lastFilter.getValue().getAllowNulls());
if (indexComponentsCount == components.size()) {
// If there is a full match, then leave it as equals filter
return new IndexEqualsFilter(new IndexFilterValue(components, allowNulls));
} else {
// Otherwise convert it to a range request
if (indexType == HASH) {
return null;
}
List<Expression> fromComponents = components;
List<Expression> toComponents = new ArrayList<>(components);
List<Boolean> fromAllowNulls = allowNulls;
List<Boolean> toAllowNulls = new ArrayList<>(fromAllowNulls);
addInfiniteRanges(fromComponents, fromAllowNulls, true, toComponents, toAllowNulls, true, indexComponentsCount);
return new IndexRangeFilter(new IndexFilterValue(fromComponents, fromAllowNulls), true, new IndexFilterValue(toComponents, toAllowNulls), true);
}
}
use of com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter in project hazelcast by hazelcast.
the class IndexResolver method createIndexFilterForSingleRange.
@SuppressWarnings("unchecked")
@Nonnull
private static IndexFilter createIndexFilterForSingleRange(Range<?> range, QueryDataType hazelcastType) {
Expression<?> lowerBound = ConstantExpression.create(range.lowerEndpoint(), hazelcastType);
IndexFilterValue lowerBound0 = new IndexFilterValue(singletonList(lowerBound), singletonList(false));
if (isSingletonRange(range)) {
return new IndexEqualsFilter(lowerBound0);
}
Expression<?> upperBound = ConstantExpression.create(range.upperEndpoint(), hazelcastType);
IndexFilterValue upperBound0 = new IndexFilterValue(singletonList(upperBound), singletonList(false));
return new IndexRangeFilter(lowerBound0, range.lowerBoundType() == BoundType.CLOSED, upperBound0, range.upperBoundType() == BoundType.CLOSED);
}
use of com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter in project hazelcast by hazelcast.
the class IndexResolver method prepareSingleColumnCandidateBooleanIsTrueFalse.
/**
* Prepare a candidate for {@code IS (NOT) TRUE/FALSE} expression.
* <p>
* The fundamental observation is that boolean column may have only three values - TRUE/FALSE/NULL. Therefore, every
* such expression could be converted to equivalent equals or IN predicate:
* - IS TRUE -> EQUALS(TRUE)
* - IS FALSE -> EQUALS(FALSE)
* - IS NOT TRUE -> IN(EQUALS(FALSE), EQUALS(NULL))
* - IS NOT FALSE -> IN(EQUALS(TRUE), EQUALS(NULL))
*
* @param exp original expression, e.g. {col IS TRUE}
* @param operand operand, e.g. {col}; CAST must be unwrapped before the method is invoked
* @param kind expression type
* @return candidate or {@code null}
*/
private static IndexComponentCandidate prepareSingleColumnCandidateBooleanIsTrueFalse(RexNode exp, RexNode operand, SqlKind kind) {
if (operand.getKind() != SqlKind.INPUT_REF) {
// The operand is not a column, e.g. {'true' IS TRUE}, index cannot be used
return null;
}
if (operand.getType().getSqlTypeName() != SqlTypeName.BOOLEAN) {
// The column is not of BOOLEAN type. We should never hit this branch normally. Added here only for safety.
return null;
}
int columnIndex = ((RexInputRef) operand).getIndex();
IndexFilter filter;
switch(kind) {
case IS_TRUE:
filter = new IndexEqualsFilter(new IndexFilterValue(singletonList(ConstantExpression.create(true, QueryDataType.BOOLEAN)), singletonList(false)));
break;
case IS_FALSE:
filter = new IndexEqualsFilter(new IndexFilterValue(singletonList(ConstantExpression.create(false, QueryDataType.BOOLEAN)), singletonList(false)));
break;
case IS_NOT_TRUE:
filter = new IndexInFilter(new IndexEqualsFilter(new IndexFilterValue(singletonList(ConstantExpression.create(false, QueryDataType.BOOLEAN)), singletonList(false))), new IndexEqualsFilter(new IndexFilterValue(singletonList(ConstantExpression.create(null, QueryDataType.BOOLEAN)), singletonList(true))));
break;
default:
assert kind == SqlKind.IS_NOT_FALSE;
filter = new IndexInFilter(new IndexEqualsFilter(new IndexFilterValue(singletonList(ConstantExpression.create(true, QueryDataType.BOOLEAN)), singletonList(false))), new IndexEqualsFilter(new IndexFilterValue(singletonList(ConstantExpression.create(null, QueryDataType.BOOLEAN)), singletonList(true))));
}
return new IndexComponentCandidate(exp, columnIndex, filter);
}
use of com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter in project hazelcast by hazelcast.
the class IndexResolver method prepareSingleColumnCandidateComparison.
/**
* Try creating a candidate filter for comparison operator.
*
* @param exp the original expression
* @param kind expression kine (=, >, <, >=, <=)
* @param operand1 the first operand (CAST must be unwrapped before the method is invoked)
* @param operand2 the second operand (CAST must be unwrapped before the method is invoked)
* @param parameterMetadata parameter metadata for expressions like {a>?}
* @return candidate or {@code null}
*/
private static IndexComponentCandidate prepareSingleColumnCandidateComparison(RexNode exp, SqlKind kind, RexNode operand1, RexNode operand2, QueryParameterMetadata parameterMetadata) {
// The condition (kind) is changed accordingly (e.g. ">" to "<").
if (operand1.getKind() != SqlKind.INPUT_REF && operand2.getKind() == SqlKind.INPUT_REF) {
kind = inverseIndexConditionKind(kind);
RexNode tmp = operand1;
operand1 = operand2;
operand2 = tmp;
}
if (operand1.getKind() != SqlKind.INPUT_REF) {
// No columns in the expression, index cannot be used. E.g. {'a' > 'b'}
return null;
}
int columnIndex = ((RexInputRef) operand1).getIndex();
if (!IndexRexVisitor.isValid(operand2)) {
// E.g. {column_a > column_b}.
return null;
}
// Convert the second operand into Hazelcast expression. The expression will be evaluated once before index scan is
// initiated, to construct the proper filter for index lookup.
Expression<?> filterValue = convertToExpression(operand2, parameterMetadata);
if (filterValue == null) {
// tree to Hazelcast plan.
return null;
}
// Create the value that will be passed to filters. Not that "allowNulls=false" here, because any NULL in the comparison
// operator never returns "TRUE" and hence always returns an empty result set.
IndexFilterValue filterValue0 = new IndexFilterValue(singletonList(filterValue), singletonList(false));
IndexFilter filter;
switch(kind) {
case EQUALS:
filter = new IndexEqualsFilter(filterValue0);
break;
case GREATER_THAN:
filter = new IndexRangeFilter(filterValue0, false, null, false);
break;
case GREATER_THAN_OR_EQUAL:
filter = new IndexRangeFilter(filterValue0, true, null, false);
break;
case LESS_THAN:
filter = new IndexRangeFilter(null, false, filterValue0, false);
break;
default:
assert kind == SqlKind.LESS_THAN_OR_EQUAL;
filter = new IndexRangeFilter(null, false, filterValue0, true);
}
return new IndexComponentCandidate(exp, columnIndex, filter);
}
use of com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter in project hazelcast by hazelcast.
the class IndexResolver method prepareSingleColumnCandidateOr.
/**
* Prepare candidate for OR expression if possible.
* <p>
* We support only equality conditions on the same columns. Ranges and conditions on different columns (aka "index joins")
* are not supported.
*
* @param exp the OR expression
* @param nodes components of the OR expression
* @param parameterMetadata parameter metadata
* @return candidate or {code null}
*/
private static IndexComponentCandidate prepareSingleColumnCandidateOr(RexNode exp, List<RexNode> nodes, QueryParameterMetadata parameterMetadata) {
Integer columnIndex = null;
List<IndexFilter> filters = new ArrayList<>();
for (RexNode node : nodes) {
IndexComponentCandidate candidate = prepareSingleColumnCandidate(node, parameterMetadata);
if (candidate == null) {
// The component of the OR expression cannot be used by any index implementation
return null;
}
IndexFilter candidateFilter = candidate.getFilter();
if (!(candidateFilter instanceof IndexEqualsFilter || candidateFilter instanceof IndexInFilter)) {
// Support only equality for ORs
return null;
}
// Make sure that all '=' expressions relate to a single column
if (columnIndex == null) {
columnIndex = candidate.getColumnIndex();
} else if (columnIndex != candidate.getColumnIndex()) {
return null;
}
// Flatten. E.g. ((a=1 OR a=2) OR a=3) is parsed into IN(1, 2) and OR(3), that is then flatten into IN(1, 2, 3)
if (candidateFilter instanceof IndexEqualsFilter) {
filters.add(candidateFilter);
} else {
filters.addAll(((IndexInFilter) candidateFilter).getFilters());
}
}
assert columnIndex != null;
IndexInFilter inFilter = new IndexInFilter(filters);
return new IndexComponentCandidate(exp, columnIndex, inFilter);
}
Aggregations