Search in sources :

Example 1 with IndexFilterValue

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);
    }
}
Also used : IndexRangeFilter(com.hazelcast.sql.impl.exec.scan.index.IndexRangeFilter) IndexEqualsFilter(com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter) Expression(com.hazelcast.sql.impl.expression.Expression) RexToExpression(com.hazelcast.jet.sql.impl.opt.physical.visitor.RexToExpression) ConstantExpression(com.hazelcast.sql.impl.expression.ConstantExpression) IndexFilterValue(com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue) ArrayList(java.util.ArrayList)

Example 2 with IndexFilterValue

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);
}
Also used : IndexRangeFilter(com.hazelcast.sql.impl.exec.scan.index.IndexRangeFilter) IndexEqualsFilter(com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter) IndexFilterValue(com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue) Nonnull(javax.annotation.Nonnull)

Example 3 with IndexFilterValue

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);
}
Also used : IndexEqualsFilter(com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter) IndexFilterValue(com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue) RexInputRef(org.apache.calcite.rex.RexInputRef) IndexInFilter(com.hazelcast.sql.impl.exec.scan.index.IndexInFilter) IndexFilter(com.hazelcast.sql.impl.exec.scan.index.IndexFilter)

Example 4 with IndexFilterValue

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);
}
Also used : IndexRangeFilter(com.hazelcast.sql.impl.exec.scan.index.IndexRangeFilter) IndexEqualsFilter(com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter) IndexFilterValue(com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue) RexInputRef(org.apache.calcite.rex.RexInputRef) IndexFilter(com.hazelcast.sql.impl.exec.scan.index.IndexFilter) RexNode(org.apache.calcite.rex.RexNode)

Example 5 with IndexFilterValue

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());
}
Also used : IndexRangeFilter(com.hazelcast.sql.impl.exec.scan.index.IndexRangeFilter) Expression(com.hazelcast.sql.impl.expression.Expression) RexToExpression(com.hazelcast.jet.sql.impl.opt.physical.visitor.RexToExpression) ConstantExpression(com.hazelcast.sql.impl.expression.ConstantExpression) IndexFilterValue(com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue) ArrayList(java.util.ArrayList)

Aggregations

IndexFilterValue (com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue)17 IndexEqualsFilter (com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter)9 ParallelJVMTest (com.hazelcast.test.annotation.ParallelJVMTest)8 QuickTest (com.hazelcast.test.annotation.QuickTest)8 Test (org.junit.Test)8 IndexRangeFilter (com.hazelcast.sql.impl.exec.scan.index.IndexRangeFilter)7 IndexFilter (com.hazelcast.sql.impl.exec.scan.index.IndexFilter)3 IndexInFilter (com.hazelcast.sql.impl.exec.scan.index.IndexInFilter)3 Expression (com.hazelcast.sql.impl.expression.Expression)3 ExpressionEvalContext (com.hazelcast.sql.impl.expression.ExpressionEvalContext)3 ArrayList (java.util.ArrayList)3 RexInputRef (org.apache.calcite.rex.RexInputRef)3 RexToExpression (com.hazelcast.jet.sql.impl.opt.physical.visitor.RexToExpression)2 ConstantExpression (com.hazelcast.sql.impl.expression.ConstantExpression)2 ArrayDataSerializableFactory (com.hazelcast.internal.serialization.impl.ArrayDataSerializableFactory)1 ConstructorFunction (com.hazelcast.internal.util.ConstructorFunction)1 QueryException (com.hazelcast.sql.impl.QueryException)1 ExtractFunction (com.hazelcast.sql.impl.expression.datetime.ExtractFunction)1 ToEpochMillisFunction (com.hazelcast.sql.impl.expression.datetime.ToEpochMillisFunction)1 ToTimestampTzFunction (com.hazelcast.sql.impl.expression.datetime.ToTimestampTzFunction)1