Search in sources :

Example 1 with IndexFilter

use of com.hazelcast.sql.impl.exec.scan.index.IndexFilter 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 2 with IndexFilter

use of com.hazelcast.sql.impl.exec.scan.index.IndexFilter 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 3 with IndexFilter

use of com.hazelcast.sql.impl.exec.scan.index.IndexFilter in project hazelcast by hazelcast.

the class IndexResolver method prepareSingleColumnSearchCandidateComparison.

@SuppressWarnings({ "ConstantConditions", "UnstableApiUsage" })
private static IndexComponentCandidate prepareSingleColumnSearchCandidateComparison(RexNode exp, RexNode operand1, RexNode operand2) {
    // SARG is supported only for literals, not for dynamic parameters
    if (operand1.getKind() != SqlKind.INPUT_REF || operand2.getKind() != SqlKind.LITERAL) {
        return null;
    }
    int columnIndex = ((RexInputRef) operand1).getIndex();
    RexLiteral literal = (RexLiteral) operand2;
    QueryDataType hazelcastType = HazelcastTypeUtils.toHazelcastType(literal.getType());
    RangeSet<?> rangeSet = RexToExpression.extractRangeFromSearch(literal);
    if (rangeSet == null) {
        return null;
    }
    Set<? extends Range<?>> ranges = rangeSet.asRanges();
    IndexFilter indexFilter;
    if (ranges.size() == 1) {
        indexFilter = createIndexFilterForSingleRange(Iterables.getFirst(ranges, null), hazelcastType);
    } else if (ranges.stream().allMatch(IndexResolver::isSingletonRange)) {
        indexFilter = new IndexInFilter(toList(ranges, range -> createIndexFilterForSingleRange(range, hazelcastType)));
    } else {
        // No support for IndexInFilter with multiple IndexFilterForSingleRanges
        return null;
    }
    return new IndexComponentCandidate(exp, columnIndex, indexFilter);
}
Also used : RangeSet(com.google.common.collect.RangeSet) QueryDataTypeFamily(com.hazelcast.sql.impl.type.QueryDataTypeFamily) OptUtils.getCluster(com.hazelcast.jet.sql.impl.opt.OptUtils.getCluster) QueryParameterMetadata(com.hazelcast.sql.impl.QueryParameterMetadata) Collections.singletonList(java.util.Collections.singletonList) HazelcastTable(com.hazelcast.jet.sql.impl.schema.HazelcastTable) TypeConverters(com.hazelcast.query.impl.TypeConverters) RexUtil(org.apache.calcite.rex.RexUtil) RexNode(org.apache.calcite.rex.RexNode) Map(java.util.Map) IndexRangeFilter(com.hazelcast.sql.impl.exec.scan.index.IndexRangeFilter) QueryDataTypeUtils(com.hazelcast.sql.impl.type.QueryDataTypeUtils) HASH(com.hazelcast.config.IndexType.HASH) IndexEqualsFilter(com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter) RexToExpressionVisitor(com.hazelcast.jet.sql.impl.opt.physical.visitor.RexToExpressionVisitor) PlanNodeFieldTypeProvider(com.hazelcast.sql.impl.plan.node.PlanNodeFieldTypeProvider) RelTraitSet(org.apache.calcite.plan.RelTraitSet) SqlKind(org.apache.calcite.sql.SqlKind) HazelcastTypeUtils(com.hazelcast.jet.sql.impl.validate.types.HazelcastTypeUtils) RexLiteral(org.apache.calcite.rex.RexLiteral) Collection(java.util.Collection) Range(com.google.common.collect.Range) Set(java.util.Set) RelFieldCollation(org.apache.calcite.rel.RelFieldCollation) Collectors(java.util.stream.Collectors) IndexInFilter(com.hazelcast.sql.impl.exec.scan.index.IndexInFilter) RexInputRef(org.apache.calcite.rex.RexInputRef) List(java.util.List) FullScanLogicalRel(com.hazelcast.jet.sql.impl.opt.logical.FullScanLogicalRel) BoundType(com.google.common.collect.BoundType) RelCollation(org.apache.calcite.rel.RelCollation) MapTableIndex(com.hazelcast.sql.impl.schema.map.MapTableIndex) TRUE(java.lang.Boolean.TRUE) RexCall(org.apache.calcite.rex.RexCall) OptUtils.createRelTable(com.hazelcast.jet.sql.impl.opt.OptUtils.createRelTable) Iterables(com.google.common.collect.Iterables) IndexFilterValue(com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue) IndexFilter(com.hazelcast.sql.impl.exec.scan.index.IndexFilter) QueryDataType(com.hazelcast.sql.impl.type.QueryDataType) HashMap(java.util.HashMap) POSITIVE_INFINITY(com.hazelcast.query.impl.CompositeValue.POSITIVE_INFINITY) RelOptUtil(org.apache.calcite.plan.RelOptUtil) RelOptTable(org.apache.calcite.plan.RelOptTable) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) IndexType(com.hazelcast.config.IndexType) BiTuple(com.hazelcast.internal.util.BiTuple) Expression(com.hazelcast.sql.impl.expression.Expression) Nonnull(javax.annotation.Nonnull) ComparableIdentifiedDataSerializable(com.hazelcast.query.impl.ComparableIdentifiedDataSerializable) RelCollations(org.apache.calcite.rel.RelCollations) RexToExpression(com.hazelcast.jet.sql.impl.opt.physical.visitor.RexToExpression) RelDataType(org.apache.calcite.rel.type.RelDataType) FALSE(java.lang.Boolean.FALSE) HazelcastRelOptTable(com.hazelcast.jet.sql.impl.schema.HazelcastRelOptTable) RelCollationTraitDef(org.apache.calcite.rel.RelCollationTraitDef) SqlTypeName(org.apache.calcite.sql.type.SqlTypeName) SORTED(com.hazelcast.config.IndexType.SORTED) Util.toList(com.hazelcast.jet.impl.util.Util.toList) RexBuilder(org.apache.calcite.rex.RexBuilder) OptUtils(com.hazelcast.jet.sql.impl.opt.OptUtils) IndexScanMapPhysicalRel(com.hazelcast.jet.sql.impl.opt.physical.IndexScanMapPhysicalRel) RelNode(org.apache.calcite.rel.RelNode) Direction(org.apache.calcite.rel.RelFieldCollation.Direction) ASCENDING(org.apache.calcite.rel.RelFieldCollation.Direction.ASCENDING) TreeMap(java.util.TreeMap) NEGATIVE_INFINITY(com.hazelcast.query.impl.CompositeValue.NEGATIVE_INFINITY) ConstantExpression(com.hazelcast.sql.impl.expression.ConstantExpression) DESCENDING(org.apache.calcite.rel.RelFieldCollation.Direction.DESCENDING) Collections(java.util.Collections) RexLiteral(org.apache.calcite.rex.RexLiteral) QueryDataType(com.hazelcast.sql.impl.type.QueryDataType) 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 IndexFilter

use of com.hazelcast.sql.impl.exec.scan.index.IndexFilter 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)

Example 5 with IndexFilter

use of com.hazelcast.sql.impl.exec.scan.index.IndexFilter in project hazelcast by hazelcast.

the class IndexInFilterTest method testContent.

@Test
public void testContent() {
    List<IndexFilter> filters = Collections.singletonList(new IndexEqualsFilter(intValue(1)));
    IndexInFilter filter = new IndexInFilter(filters);
    assertSame(filters, filter.getFilters());
}
Also used : IndexEqualsFilter(com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter) IndexInFilter(com.hazelcast.sql.impl.exec.scan.index.IndexInFilter) IndexFilter(com.hazelcast.sql.impl.exec.scan.index.IndexFilter) ParallelJVMTest(com.hazelcast.test.annotation.ParallelJVMTest) QuickTest(com.hazelcast.test.annotation.QuickTest) Test(org.junit.Test)

Aggregations

IndexFilter (com.hazelcast.sql.impl.exec.scan.index.IndexFilter)15 ArrayList (java.util.ArrayList)10 IndexEqualsFilter (com.hazelcast.sql.impl.exec.scan.index.IndexEqualsFilter)9 IndexRangeFilter (com.hazelcast.sql.impl.exec.scan.index.IndexRangeFilter)8 ParallelJVMTest (com.hazelcast.test.annotation.ParallelJVMTest)7 QuickTest (com.hazelcast.test.annotation.QuickTest)7 Test (org.junit.Test)7 IndexConfig (com.hazelcast.config.IndexConfig)6 JobConfig (com.hazelcast.jet.config.JobConfig)6 MapIndexScanMetadata (com.hazelcast.sql.impl.exec.scan.MapIndexScanMetadata)6 IndexInFilter (com.hazelcast.sql.impl.exec.scan.index.IndexInFilter)6 JetSqlRow (com.hazelcast.sql.impl.row.JetSqlRow)6 IndexFilterValue (com.hazelcast.sql.impl.exec.scan.index.IndexFilterValue)4 RexInputRef (org.apache.calcite.rex.RexInputRef)4 RexNode (org.apache.calcite.rex.RexNode)4 IndexScanMapPhysicalRel (com.hazelcast.jet.sql.impl.opt.physical.IndexScanMapPhysicalRel)2 HazelcastRelOptTable (com.hazelcast.jet.sql.impl.schema.HazelcastRelOptTable)2 HazelcastTable (com.hazelcast.jet.sql.impl.schema.HazelcastTable)2 BoundType (com.google.common.collect.BoundType)1 Iterables (com.google.common.collect.Iterables)1