Search in sources :

Example 1 with BoundRefKey

use of org.apache.druid.sql.calcite.filtration.BoundRefKey in project druid by druid-io.

the class Expressions method buildTimeFloorFilter.

/**
 * Build a filter for an expression like FLOOR(column TO granularity) [operator] rhsMillis
 */
private static DimFilter buildTimeFloorFilter(final String column, final Granularity granularity, final SqlKind operatorKind, final long rhsMillis) {
    final BoundRefKey boundRefKey = new BoundRefKey(column, null, StringComparators.NUMERIC);
    final Interval rhsInterval = granularity.bucket(DateTimes.utc(rhsMillis));
    // Is rhs aligned on granularity boundaries?
    final boolean rhsAligned = rhsInterval.getStartMillis() == rhsMillis;
    return getBoundTimeDimFilter(operatorKind, boundRefKey, rhsInterval, rhsAligned);
}
Also used : BoundRefKey(org.apache.druid.sql.calcite.filtration.BoundRefKey) Interval(org.joda.time.Interval)

Example 2 with BoundRefKey

use of org.apache.druid.sql.calcite.filtration.BoundRefKey in project druid by druid-io.

the class Expressions method toSimpleLeafFilter.

/**
 * Translates to a simple leaf filter, i.e. not an "expression" type filter. Note that the filter may still
 * reference expression virtual columns, if and only if "virtualColumnRegistry" is defined.
 *
 * @param plannerContext        planner context
 * @param rowSignature          input row signature
 * @param virtualColumnRegistry re-usable virtual column references, may be null if virtual columns aren't allowed
 * @param rexNode               Calcite row expression
 */
@Nullable
private static DimFilter toSimpleLeafFilter(final PlannerContext plannerContext, final RowSignature rowSignature, @Nullable final VirtualColumnRegistry virtualColumnRegistry, final RexNode rexNode) {
    final SqlKind kind = rexNode.getKind();
    if (kind == SqlKind.IS_TRUE || kind == SqlKind.IS_NOT_FALSE) {
        return toSimpleLeafFilter(plannerContext, rowSignature, virtualColumnRegistry, Iterables.getOnlyElement(((RexCall) rexNode).getOperands()));
    } else if (kind == SqlKind.IS_FALSE || kind == SqlKind.IS_NOT_TRUE) {
        return new NotDimFilter(toSimpleLeafFilter(plannerContext, rowSignature, virtualColumnRegistry, Iterables.getOnlyElement(((RexCall) rexNode).getOperands())));
    } else if (kind == SqlKind.IS_NULL || kind == SqlKind.IS_NOT_NULL) {
        final RexNode operand = Iterables.getOnlyElement(((RexCall) rexNode).getOperands());
        final DruidExpression druidExpression = toDruidExpression(plannerContext, rowSignature, operand);
        if (druidExpression == null) {
            return null;
        }
        final DimFilter equalFilter;
        if (druidExpression.isSimpleExtraction()) {
            equalFilter = new SelectorDimFilter(druidExpression.getSimpleExtraction().getColumn(), NullHandling.defaultStringValue(), druidExpression.getSimpleExtraction().getExtractionFn());
        } else if (virtualColumnRegistry != null) {
            final String virtualColumn = virtualColumnRegistry.getOrCreateVirtualColumnForExpression(druidExpression, operand.getType());
            equalFilter = new SelectorDimFilter(virtualColumn, NullHandling.defaultStringValue(), null);
        } else {
            return null;
        }
        return kind == SqlKind.IS_NOT_NULL ? new NotDimFilter(equalFilter) : equalFilter;
    } else if (kind == SqlKind.EQUALS || kind == SqlKind.NOT_EQUALS || kind == SqlKind.GREATER_THAN || kind == SqlKind.GREATER_THAN_OR_EQUAL || kind == SqlKind.LESS_THAN || kind == SqlKind.LESS_THAN_OR_EQUAL) {
        final List<RexNode> operands = ((RexCall) rexNode).getOperands();
        Preconditions.checkState(operands.size() == 2, "Expected 2 operands, got[%,d]", operands.size());
        boolean flip = false;
        RexNode lhs = operands.get(0);
        RexNode rhs = operands.get(1);
        if (lhs.getKind() == SqlKind.LITERAL && rhs.getKind() != SqlKind.LITERAL) {
            // swap lhs, rhs
            RexNode x = lhs;
            lhs = rhs;
            rhs = x;
            flip = true;
        }
        // Flip operator, maybe.
        final SqlKind flippedKind;
        if (flip) {
            switch(kind) {
                case EQUALS:
                case NOT_EQUALS:
                    flippedKind = kind;
                    break;
                case GREATER_THAN:
                    flippedKind = SqlKind.LESS_THAN;
                    break;
                case GREATER_THAN_OR_EQUAL:
                    flippedKind = SqlKind.LESS_THAN_OR_EQUAL;
                    break;
                case LESS_THAN:
                    flippedKind = SqlKind.GREATER_THAN;
                    break;
                case LESS_THAN_OR_EQUAL:
                    flippedKind = SqlKind.GREATER_THAN_OR_EQUAL;
                    break;
                default:
                    throw new ISE("Kind[%s] not expected here", kind);
            }
        } else {
            flippedKind = kind;
        }
        // rhs must be a literal
        if (rhs.getKind() != SqlKind.LITERAL) {
            return null;
        }
        // Translate lhs to a DruidExpression.
        final DruidExpression lhsExpression = toDruidExpression(plannerContext, rowSignature, lhs);
        if (lhsExpression == null) {
            return null;
        }
        // Special handling for filters on FLOOR(__time TO granularity).
        final Granularity queryGranularity = toQueryGranularity(lhsExpression, plannerContext.getExprMacroTable());
        if (queryGranularity != null) {
            // lhs is FLOOR(__time TO granularity); rhs must be a timestamp
            final long rhsMillis = Calcites.calciteDateTimeLiteralToJoda(rhs, plannerContext.getTimeZone()).getMillis();
            return buildTimeFloorFilter(ColumnHolder.TIME_COLUMN_NAME, queryGranularity, flippedKind, rhsMillis);
        }
        final String column;
        final ExtractionFn extractionFn;
        if (lhsExpression.isSimpleExtraction()) {
            column = lhsExpression.getSimpleExtraction().getColumn();
            extractionFn = lhsExpression.getSimpleExtraction().getExtractionFn();
        } else if (virtualColumnRegistry != null) {
            column = virtualColumnRegistry.getOrCreateVirtualColumnForExpression(lhsExpression, lhs.getType());
            extractionFn = null;
        } else {
            return null;
        }
        if (column.equals(ColumnHolder.TIME_COLUMN_NAME) && extractionFn instanceof TimeFormatExtractionFn) {
            // Check if we can strip the extractionFn and convert the filter to a direct filter on __time.
            // This allows potential conversion to query-level "intervals" later on, which is ideal for Druid queries.
            final Granularity granularity = ExtractionFns.toQueryGranularity(extractionFn);
            if (granularity != null) {
                // lhs is FLOOR(__time TO granularity); rhs must be a timestamp
                final long rhsMillis = Calcites.calciteDateTimeLiteralToJoda(rhs, plannerContext.getTimeZone()).getMillis();
                final Interval rhsInterval = granularity.bucket(DateTimes.utc(rhsMillis));
                // Is rhs aligned on granularity boundaries?
                final boolean rhsAligned = rhsInterval.getStartMillis() == rhsMillis;
                // Create a BoundRefKey that strips the extractionFn and compares __time as a number.
                final BoundRefKey boundRefKey = new BoundRefKey(column, null, StringComparators.NUMERIC);
                return getBoundTimeDimFilter(flippedKind, boundRefKey, rhsInterval, rhsAligned);
            }
        }
        final String val;
        final RexLiteral rhsLiteral = (RexLiteral) rhs;
        if (SqlTypeName.NUMERIC_TYPES.contains(rhsLiteral.getTypeName())) {
            val = String.valueOf(RexLiteral.value(rhsLiteral));
        } else if (SqlTypeName.CHAR_TYPES.contains(rhsLiteral.getTypeName())) {
            val = String.valueOf(RexLiteral.stringValue(rhsLiteral));
        } else if (SqlTypeName.TIMESTAMP == rhsLiteral.getTypeName() || SqlTypeName.DATE == rhsLiteral.getTypeName()) {
            val = String.valueOf(Calcites.calciteDateTimeLiteralToJoda(rhsLiteral, plannerContext.getTimeZone()).getMillis());
        } else {
            // Don't know how to filter on this kind of literal.
            return null;
        }
        // Numeric lhs needs a numeric comparison.
        final StringComparator comparator = Calcites.getStringComparatorForRelDataType(lhs.getType());
        final BoundRefKey boundRefKey = new BoundRefKey(column, extractionFn, comparator);
        final DimFilter filter;
        // Always use BoundDimFilters, to simplify filter optimization later (it helps to remember the comparator).
        switch(flippedKind) {
            case EQUALS:
                filter = Bounds.equalTo(boundRefKey, val);
                break;
            case NOT_EQUALS:
                filter = new NotDimFilter(Bounds.equalTo(boundRefKey, val));
                break;
            case GREATER_THAN:
                filter = Bounds.greaterThan(boundRefKey, val);
                break;
            case GREATER_THAN_OR_EQUAL:
                filter = Bounds.greaterThanOrEqualTo(boundRefKey, val);
                break;
            case LESS_THAN:
                filter = Bounds.lessThan(boundRefKey, val);
                break;
            case LESS_THAN_OR_EQUAL:
                filter = Bounds.lessThanOrEqualTo(boundRefKey, val);
                break;
            default:
                throw new IllegalStateException("Shouldn't have got here");
        }
        return filter;
    } else if (rexNode instanceof RexCall) {
        final SqlOperator operator = ((RexCall) rexNode).getOperator();
        final SqlOperatorConversion conversion = plannerContext.getOperatorTable().lookupOperatorConversion(operator);
        if (conversion == null) {
            return null;
        } else {
            return conversion.toDruidFilter(plannerContext, rowSignature, virtualColumnRegistry, rexNode);
        }
    } else {
        return null;
    }
}
Also used : TimeFormatExtractionFn(org.apache.druid.query.extraction.TimeFormatExtractionFn) RexLiteral(org.apache.calcite.rex.RexLiteral) NotDimFilter(org.apache.druid.query.filter.NotDimFilter) SqlOperator(org.apache.calcite.sql.SqlOperator) SqlKind(org.apache.calcite.sql.SqlKind) Granularity(org.apache.druid.java.util.common.granularity.Granularity) StringComparator(org.apache.druid.query.ordering.StringComparator) RexCall(org.apache.calcite.rex.RexCall) ExtractionFn(org.apache.druid.query.extraction.ExtractionFn) TimeFormatExtractionFn(org.apache.druid.query.extraction.TimeFormatExtractionFn) SelectorDimFilter(org.apache.druid.query.filter.SelectorDimFilter) ArrayList(java.util.ArrayList) List(java.util.List) ISE(org.apache.druid.java.util.common.ISE) BoundRefKey(org.apache.druid.sql.calcite.filtration.BoundRefKey) SelectorDimFilter(org.apache.druid.query.filter.SelectorDimFilter) AndDimFilter(org.apache.druid.query.filter.AndDimFilter) NotDimFilter(org.apache.druid.query.filter.NotDimFilter) DimFilter(org.apache.druid.query.filter.DimFilter) OrDimFilter(org.apache.druid.query.filter.OrDimFilter) ExpressionDimFilter(org.apache.druid.query.filter.ExpressionDimFilter) RexNode(org.apache.calcite.rex.RexNode) Interval(org.joda.time.Interval) Nullable(javax.annotation.Nullable)

Aggregations

BoundRefKey (org.apache.druid.sql.calcite.filtration.BoundRefKey)2 Interval (org.joda.time.Interval)2 ArrayList (java.util.ArrayList)1 List (java.util.List)1 Nullable (javax.annotation.Nullable)1 RexCall (org.apache.calcite.rex.RexCall)1 RexLiteral (org.apache.calcite.rex.RexLiteral)1 RexNode (org.apache.calcite.rex.RexNode)1 SqlKind (org.apache.calcite.sql.SqlKind)1 SqlOperator (org.apache.calcite.sql.SqlOperator)1 ISE (org.apache.druid.java.util.common.ISE)1 Granularity (org.apache.druid.java.util.common.granularity.Granularity)1 ExtractionFn (org.apache.druid.query.extraction.ExtractionFn)1 TimeFormatExtractionFn (org.apache.druid.query.extraction.TimeFormatExtractionFn)1 AndDimFilter (org.apache.druid.query.filter.AndDimFilter)1 DimFilter (org.apache.druid.query.filter.DimFilter)1 ExpressionDimFilter (org.apache.druid.query.filter.ExpressionDimFilter)1 NotDimFilter (org.apache.druid.query.filter.NotDimFilter)1 OrDimFilter (org.apache.druid.query.filter.OrDimFilter)1 SelectorDimFilter (org.apache.druid.query.filter.SelectorDimFilter)1