use of com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue 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.IndexFilterValue 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.IndexFilterValue 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.IndexFilterValue 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.IndexFilterValue in project hazelcast by hazelcast.
the class IndexResolver method composeRangeFilter.
/**
* Create the composite range filter from the given per-column filters.
* <p>
* If the number of per-column filters if less than the number of index components, then infinite ranges are added
* to the missing components.
* <p>
* Consider that we have two per-column filter as input: {@code {a=1}, {b>2 AND b<3}}.
* <p>
* If the index is defined as {@code {a, b}}, then the resulting filter would be {@code {a=1, b>2 AND a=1, b<3}}.
* <p>
* If the index is defined as {@code {a, b, c}}, then the resulting filter would be
* {@code {a=1, b>2, c>NEGATIVE_INFINITY AND a=1, b<3, c<POSITIVE_INFINITY}}.
*
* @param filters all per-column filters
* @param lastFilter the last filter (range)
* @param componentsCount number of components in the filter
* @return range filter
*/
private static IndexFilter composeRangeFilter(List<IndexFilter> filters, IndexRangeFilter lastFilter, int componentsCount) {
// Flatten non-terminal components.
List<Expression> components = new ArrayList<>(filters.size());
List<Boolean> allowNulls = new ArrayList<>();
fillNonTerminalComponents(filters, components, allowNulls);
// Add value of the current filter.
List<Expression> fromComponents = components;
List<Expression> toComponents = new ArrayList<>(components);
List<Boolean> fromAllowNulls = allowNulls;
List<Boolean> toAllowNulls = new ArrayList<>(fromAllowNulls);
if (lastFilter.getFrom() != null) {
fromComponents.add(lastFilter.getFrom().getComponents().get(0));
fromAllowNulls.add(false);
} else {
if (componentsCount == 1) {
fromComponents.add(ConstantExpression.create(NEGATIVE_INFINITY, QueryDataType.OBJECT));
fromAllowNulls.add(false);
} else {
// In composite indexes null values are not stored separately. Therefore, we need to filter them out.
fromComponents.add(ConstantExpression.create(null, QueryDataType.OBJECT));
fromAllowNulls.add(true);
}
}
if (lastFilter.getTo() != null) {
toComponents.add(lastFilter.getTo().getComponents().get(0));
} else {
toComponents.add(ConstantExpression.create(POSITIVE_INFINITY, QueryDataType.OBJECT));
}
toAllowNulls.add(false);
// Fill missing part of the range request.
addInfiniteRanges(fromComponents, fromAllowNulls, lastFilter.isFromInclusive(), toComponents, toAllowNulls, lastFilter.isToInclusive(), componentsCount);
return new IndexRangeFilter(new IndexFilterValue(fromComponents, fromAllowNulls), lastFilter.isFromInclusive(), new IndexFilterValue(toComponents, toAllowNulls), lastFilter.isToInclusive());
}
Aggregations