Search in sources :

Example 1 with IndexEqualsFilter

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);
    }
}
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 IndexEqualsFilter

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);
}
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 IndexEqualsFilter

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);
}
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 IndexEqualsFilter

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);
}
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 IndexEqualsFilter

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

Aggregations

IndexEqualsFilter (com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter)22 IndexFilterValue (com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue)9 IndexInFilter (com.hazelcast.sql.impl.exec.scan.index.IndexInFilter)9 IndexFilter (com.hazelcast.sql.impl.exec.scan.index.IndexFilter)8 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)6 ArrayList (java.util.ArrayList)6 IndexConfig (com.hazelcast.config.IndexConfig)3 ExpressionEvalContext (com.hazelcast.sql.impl.expression.ExpressionEvalContext)3 RexInputRef (org.apache.calcite.rex.RexInputRef)3 RexNode (org.apache.calcite.rex.RexNode)3 HazelcastInstance (com.hazelcast.core.HazelcastInstance)2 InternalIndex (com.hazelcast.query.impl.InternalIndex)2 QueryDataType (com.hazelcast.sql.impl.type.QueryDataType)2 ArrayDataSerializableFactory (com.hazelcast.internal.serialization.impl.ArrayDataSerializableFactory)1 ConstructorFunction (com.hazelcast.internal.util.ConstructorFunction)1 JobConfig (com.hazelcast.jet.config.JobConfig)1 RexToExpression (com.hazelcast.jet.sql.impl.opt.physical.visitor.RexToExpression)1