Search in sources :

Example 51 with ISE

use of io.druid.java.util.common.ISE in project druid by druid-io.

the class ApproxCountDistinctSqlAggregator method toDruidAggregation.

@Override
public Aggregation toDruidAggregation(final String name, final RowSignature rowSignature, final DruidOperatorTable operatorTable, final PlannerContext plannerContext, final List<Aggregation> existingAggregations, final Project project, final AggregateCall aggregateCall, final DimFilter filter) {
    final RexNode rexNode = Expressions.fromFieldAccess(rowSignature, project, Iterables.getOnlyElement(aggregateCall.getArgList()));
    final RowExtraction rex = Expressions.toRowExtraction(operatorTable, plannerContext, rowSignature.getRowOrder(), rexNode);
    if (rex == null) {
        return null;
    }
    final AggregatorFactory aggregatorFactory;
    if (rowSignature.getColumnType(rex.getColumn()) == ValueType.COMPLEX) {
        aggregatorFactory = new HyperUniquesAggregatorFactory(name, rex.getColumn());
    } else {
        final SqlTypeName sqlTypeName = rexNode.getType().getSqlTypeName();
        final ValueType outputType = Calcites.getValueTypeForSqlTypeName(sqlTypeName);
        if (outputType == null) {
            throw new ISE("Cannot translate sqlTypeName[%s] to Druid type for field[%s]", sqlTypeName, name);
        }
        final DimensionSpec dimensionSpec = rex.toDimensionSpec(rowSignature, null, ValueType.STRING);
        if (dimensionSpec == null) {
            return null;
        }
        aggregatorFactory = new CardinalityAggregatorFactory(name, ImmutableList.of(dimensionSpec), false);
    }
    return Aggregation.createFinalizable(ImmutableList.<AggregatorFactory>of(aggregatorFactory), null, new PostAggregatorFactory() {

        @Override
        public PostAggregator factorize(String outputName) {
            return new HyperUniqueFinalizingPostAggregator(outputName, name);
        }
    }).filter(filter);
}
Also used : DimensionSpec(io.druid.query.dimension.DimensionSpec) SqlTypeName(org.apache.calcite.sql.type.SqlTypeName) ValueType(io.druid.segment.column.ValueType) HyperUniquesAggregatorFactory(io.druid.query.aggregation.hyperloglog.HyperUniquesAggregatorFactory) RowExtraction(io.druid.sql.calcite.expression.RowExtraction) ISE(io.druid.java.util.common.ISE) HyperUniqueFinalizingPostAggregator(io.druid.query.aggregation.hyperloglog.HyperUniqueFinalizingPostAggregator) AggregatorFactory(io.druid.query.aggregation.AggregatorFactory) HyperUniquesAggregatorFactory(io.druid.query.aggregation.hyperloglog.HyperUniquesAggregatorFactory) CardinalityAggregatorFactory(io.druid.query.aggregation.cardinality.CardinalityAggregatorFactory) CardinalityAggregatorFactory(io.druid.query.aggregation.cardinality.CardinalityAggregatorFactory) RexNode(org.apache.calcite.rex.RexNode)

Example 52 with ISE

use of io.druid.java.util.common.ISE in project druid by druid-io.

the class Expressions method toMathExpression.

/**
   * Translate a row-expression to a Druid math expression. One day, when possible, this could be folded into
   * {@link #toRowExtraction(DruidOperatorTable, PlannerContext, List, RexNode)}.
   *
   * @param rowOrder   order of fields in the Druid rows to be extracted from
   * @param expression expression meant to be applied on top of the rows
   *
   * @return expression referring to fields in rowOrder, or null if not possible
   */
public static String toMathExpression(final List<String> rowOrder, final RexNode expression) {
    final SqlKind kind = expression.getKind();
    final SqlTypeName sqlTypeName = expression.getType().getSqlTypeName();
    if (kind == SqlKind.INPUT_REF) {
        // Translate field references.
        final RexInputRef ref = (RexInputRef) expression;
        final String columnName = rowOrder.get(ref.getIndex());
        if (columnName == null) {
            throw new ISE("WTF?! Expression referred to nonexistent index[%d]", ref.getIndex());
        }
        return String.format("\"%s\"", escape(columnName));
    } else if (kind == SqlKind.CAST || kind == SqlKind.REINTERPRET) {
        // Translate casts.
        final RexNode operand = ((RexCall) expression).getOperands().get(0);
        final String operandExpression = toMathExpression(rowOrder, operand);
        if (operandExpression == null) {
            return null;
        }
        final ExprType fromType = MATH_TYPES.get(operand.getType().getSqlTypeName());
        final ExprType toType = MATH_TYPES.get(sqlTypeName);
        if (fromType != toType) {
            return String.format("CAST(%s, '%s')", operandExpression, toType.toString());
        } else {
            return operandExpression;
        }
    } else if (kind == SqlKind.TIMES || kind == SqlKind.DIVIDE || kind == SqlKind.PLUS || kind == SqlKind.MINUS) {
        // Translate simple arithmetic.
        final List<RexNode> operands = ((RexCall) expression).getOperands();
        final String lhsExpression = toMathExpression(rowOrder, operands.get(0));
        final String rhsExpression = toMathExpression(rowOrder, operands.get(1));
        if (lhsExpression == null || rhsExpression == null) {
            return null;
        }
        final String op = ImmutableMap.of(SqlKind.TIMES, "*", SqlKind.DIVIDE, "/", SqlKind.PLUS, "+", SqlKind.MINUS, "-").get(kind);
        return String.format("(%s %s %s)", lhsExpression, op, rhsExpression);
    } else if (kind == SqlKind.OTHER_FUNCTION) {
        final String calciteFunction = ((RexCall) expression).getOperator().getName();
        final String druidFunction = MATH_FUNCTIONS.get(calciteFunction);
        final List<String> functionArgs = Lists.newArrayList();
        for (final RexNode operand : ((RexCall) expression).getOperands()) {
            final String operandExpression = toMathExpression(rowOrder, operand);
            if (operandExpression == null) {
                return null;
            }
            functionArgs.add(operandExpression);
        }
        if ("MOD".equals(calciteFunction)) {
            // Special handling for MOD, which is a function in Calcite but a binary operator in Druid.
            Preconditions.checkState(functionArgs.size() == 2, "WTF?! Expected 2 args for MOD.");
            return String.format("(%s %s %s)", functionArgs.get(0), "%", functionArgs.get(1));
        }
        if (druidFunction == null) {
            return null;
        }
        return String.format("%s(%s)", druidFunction, Joiner.on(", ").join(functionArgs));
    } else if (kind == SqlKind.LITERAL) {
        // Translate literal.
        if (SqlTypeName.NUMERIC_TYPES.contains(sqlTypeName)) {
            // Include literal numbers as-is.
            return String.valueOf(RexLiteral.value(expression));
        } else if (SqlTypeName.STRING_TYPES.contains(sqlTypeName)) {
            // Quote literal strings.
            return "\'" + escape(RexLiteral.stringValue(expression)) + "\'";
        } else {
            // Can't translate other literals.
            return null;
        }
    } else {
        // Can't translate other kinds of expressions.
        return null;
    }
}
Also used : RexCall(org.apache.calcite.rex.RexCall) SqlTypeName(org.apache.calcite.sql.type.SqlTypeName) ExprType(io.druid.math.expr.ExprType) RexInputRef(org.apache.calcite.rex.RexInputRef) ISE(io.druid.java.util.common.ISE) List(java.util.List) SqlKind(org.apache.calcite.sql.SqlKind) RexNode(org.apache.calcite.rex.RexNode)

Example 53 with ISE

use of io.druid.java.util.common.ISE in project druid by druid-io.

the class Expressions method toPostAggregator.

/**
   * Translate a Calcite row-expression to a Druid PostAggregator. One day, when possible, this could be folded
   * into {@link #toRowExtraction(DruidOperatorTable, PlannerContext, List, RexNode)} .
   *
   * @param name                              name of the PostAggregator
   * @param rowOrder                          order of fields in the Druid rows to be extracted from
   * @param finalizingPostAggregatorFactories post-aggregators that should be used for specific entries in rowOrder.
   *                                          May be empty, and individual values may be null. Missing or null values
   *                                          will lead to creation of {@link FieldAccessPostAggregator}.
   * @param expression                        expression meant to be applied on top of the rows
   *
   * @return PostAggregator or null if not possible
   */
public static PostAggregator toPostAggregator(final String name, final List<String> rowOrder, final List<PostAggregatorFactory> finalizingPostAggregatorFactories, final RexNode expression) {
    final PostAggregator retVal;
    if (expression.getKind() == SqlKind.INPUT_REF) {
        final RexInputRef ref = (RexInputRef) expression;
        final PostAggregatorFactory finalizingPostAggregatorFactory = finalizingPostAggregatorFactories.get(ref.getIndex());
        retVal = finalizingPostAggregatorFactory != null ? finalizingPostAggregatorFactory.factorize(name) : new FieldAccessPostAggregator(name, rowOrder.get(ref.getIndex()));
    } else if (expression.getKind() == SqlKind.CAST) {
        // Ignore CAST when translating to PostAggregators and hope for the best. They are really loosey-goosey with
        // types internally and there isn't much we can do to respect
        // TODO(gianm): Probably not a good idea to ignore CAST like this.
        final RexNode operand = ((RexCall) expression).getOperands().get(0);
        retVal = toPostAggregator(name, rowOrder, finalizingPostAggregatorFactories, operand);
    } else if (expression.getKind() == SqlKind.LITERAL && SqlTypeName.NUMERIC_TYPES.contains(expression.getType().getSqlTypeName())) {
        retVal = new ConstantPostAggregator(name, (Number) RexLiteral.value(expression));
    } else if (expression.getKind() == SqlKind.TIMES || expression.getKind() == SqlKind.DIVIDE || expression.getKind() == SqlKind.PLUS || expression.getKind() == SqlKind.MINUS) {
        final String fnName = ImmutableMap.<SqlKind, String>builder().put(SqlKind.TIMES, "*").put(SqlKind.DIVIDE, "quotient").put(SqlKind.PLUS, "+").put(SqlKind.MINUS, "-").build().get(expression.getKind());
        final List<PostAggregator> operands = Lists.newArrayList();
        for (RexNode operand : ((RexCall) expression).getOperands()) {
            final PostAggregator translatedOperand = toPostAggregator(null, rowOrder, finalizingPostAggregatorFactories, operand);
            if (translatedOperand == null) {
                return null;
            }
            operands.add(translatedOperand);
        }
        retVal = new ArithmeticPostAggregator(name, fnName, operands);
    } else {
        // Try converting to a math expression.
        final String mathExpression = Expressions.toMathExpression(rowOrder, expression);
        if (mathExpression == null) {
            retVal = null;
        } else {
            retVal = new ExpressionPostAggregator(name, mathExpression);
        }
    }
    if (retVal != null && name != null && !name.equals(retVal.getName())) {
        throw new ISE("WTF?! Was about to return a PostAggregator with bad name, [%s] != [%s]", name, retVal.getName());
    }
    return retVal;
}
Also used : ArithmeticPostAggregator(io.druid.query.aggregation.post.ArithmeticPostAggregator) FieldAccessPostAggregator(io.druid.query.aggregation.post.FieldAccessPostAggregator) ExpressionPostAggregator(io.druid.query.aggregation.post.ExpressionPostAggregator) ConstantPostAggregator(io.druid.query.aggregation.post.ConstantPostAggregator) PostAggregator(io.druid.query.aggregation.PostAggregator) FieldAccessPostAggregator(io.druid.query.aggregation.post.FieldAccessPostAggregator) ArithmeticPostAggregator(io.druid.query.aggregation.post.ArithmeticPostAggregator) ConstantPostAggregator(io.druid.query.aggregation.post.ConstantPostAggregator) PostAggregatorFactory(io.druid.sql.calcite.aggregation.PostAggregatorFactory) RexCall(org.apache.calcite.rex.RexCall) ExpressionPostAggregator(io.druid.query.aggregation.post.ExpressionPostAggregator) RexInputRef(org.apache.calcite.rex.RexInputRef) List(java.util.List) ISE(io.druid.java.util.common.ISE) RexNode(org.apache.calcite.rex.RexNode)

Example 54 with ISE

use of io.druid.java.util.common.ISE in project druid by druid-io.

the class Expressions method toRowExtraction.

/**
   * Translate a Calcite row-expression to a Druid row extraction. Note that this signature will probably need to
   * change once we support extractions from multiple columns.
   *
   * @param plannerContext SQL planner context
   * @param rowOrder       order of fields in the Druid rows to be extracted from
   * @param expression     expression meant to be applied on top of the rows
   *
   * @return RowExtraction or null if not possible
   */
public static RowExtraction toRowExtraction(final DruidOperatorTable operatorTable, final PlannerContext plannerContext, final List<String> rowOrder, final RexNode expression) {
    if (expression.getKind() == SqlKind.INPUT_REF) {
        final RexInputRef ref = (RexInputRef) expression;
        final String columnName = rowOrder.get(ref.getIndex());
        if (columnName == null) {
            throw new ISE("WTF?! Expression referred to nonexistent index[%d]", ref.getIndex());
        }
        return RowExtraction.of(columnName, null);
    } else if (expression.getKind() == SqlKind.CAST) {
        final RexNode operand = ((RexCall) expression).getOperands().get(0);
        if (expression.getType().getSqlTypeName() == SqlTypeName.DATE && operand.getType().getSqlTypeName() == SqlTypeName.TIMESTAMP) {
            // Handling casting TIMESTAMP to DATE by flooring to DAY.
            return FloorExtractionOperator.applyTimestampFloor(toRowExtraction(operatorTable, plannerContext, rowOrder, operand), TimeUnits.toQueryGranularity(TimeUnitRange.DAY, plannerContext.getTimeZone()));
        } else {
            // TODO(gianm): Probably not a good idea to ignore other CASTs like this.
            return toRowExtraction(operatorTable, plannerContext, rowOrder, ((RexCall) expression).getOperands().get(0));
        }
    } else {
        // Try conversion using a SqlExtractionOperator.
        final RowExtraction retVal;
        if (expression instanceof RexCall) {
            final SqlExtractionOperator extractionOperator = operatorTable.lookupExtractionOperator(expression.getKind(), ((RexCall) expression).getOperator().getName());
            retVal = extractionOperator != null ? extractionOperator.convert(operatorTable, plannerContext, rowOrder, expression) : null;
        } else {
            retVal = null;
        }
        return retVal;
    }
}
Also used : RexCall(org.apache.calcite.rex.RexCall) RexInputRef(org.apache.calcite.rex.RexInputRef) ISE(io.druid.java.util.common.ISE) RexNode(org.apache.calcite.rex.RexNode)

Example 55 with ISE

use of io.druid.java.util.common.ISE in project druid by druid-io.

the class CombineAndSimplifyBounds method doSimplify.

/**
   * Simplify BoundDimFilters that are children of an OR or an AND.
   *
   * @param children    the filters
   * @param disjunction true for disjunction, false for conjunction
   *
   * @return simplified filters
   */
private static DimFilter doSimplify(final List<DimFilter> children, boolean disjunction) {
    // Copy children list
    final List<DimFilter> newChildren = Lists.newArrayList(children);
    // Group Bound filters by dimension, extractionFn, and comparator and compute a RangeSet for each one.
    final Map<BoundRefKey, List<BoundDimFilter>> bounds = Maps.newHashMap();
    final Iterator<DimFilter> iterator = newChildren.iterator();
    while (iterator.hasNext()) {
        final DimFilter child = iterator.next();
        if (child.equals(Filtration.matchNothing())) {
            // AND with FALSE => always false, short circuit
            if (disjunction) {
                iterator.remove();
            } else {
                return Filtration.matchNothing();
            }
        } else if (child.equals(Filtration.matchEverything())) {
            // AND with TRUE => ignore
            if (disjunction) {
                return Filtration.matchEverything();
            } else {
                iterator.remove();
            }
        } else if (child instanceof BoundDimFilter) {
            final BoundDimFilter bound = (BoundDimFilter) child;
            final BoundRefKey boundRefKey = BoundRefKey.from(bound);
            List<BoundDimFilter> filterList = bounds.get(boundRefKey);
            if (filterList == null) {
                filterList = Lists.newArrayList();
                bounds.put(boundRefKey, filterList);
            }
            filterList.add(bound);
        }
    }
    // Try to simplify filters within each group.
    for (Map.Entry<BoundRefKey, List<BoundDimFilter>> entry : bounds.entrySet()) {
        final BoundRefKey boundRefKey = entry.getKey();
        final List<BoundDimFilter> filterList = entry.getValue();
        // Create a RangeSet for this group.
        final RangeSet<BoundValue> rangeSet = disjunction ? RangeSets.unionRanges(Bounds.toRanges(filterList)) : RangeSets.intersectRanges(Bounds.toRanges(filterList));
        if (rangeSet.asRanges().size() < filterList.size()) {
            // We found a simplification. Remove the old filters and add new ones.
            for (final BoundDimFilter bound : filterList) {
                if (!newChildren.remove(bound)) {
                    throw new ISE("WTF?! Tried to remove bound but couldn't?");
                }
            }
            if (rangeSet.asRanges().isEmpty()) {
                // AND with FALSE => always false, short circuit
                if (disjunction) {
                    newChildren.add(Filtration.matchNothing());
                } else {
                    return Filtration.matchNothing();
                }
            }
            for (final Range<BoundValue> range : rangeSet.asRanges()) {
                if (!range.hasLowerBound() && !range.hasUpperBound()) {
                    // OR with TRUE => always true; short circuit
                    if (disjunction) {
                        return Filtration.matchEverything();
                    } else {
                        newChildren.add(Filtration.matchEverything());
                    }
                } else {
                    newChildren.add(Bounds.toFilter(boundRefKey, range));
                }
            }
        }
    }
    Preconditions.checkState(newChildren.size() > 0, "newChildren.size > 0");
    if (newChildren.size() == 1) {
        return newChildren.get(0);
    } else {
        return disjunction ? new OrDimFilter(newChildren) : new AndDimFilter(newChildren);
    }
}
Also used : BoundDimFilter(io.druid.query.filter.BoundDimFilter) AndDimFilter(io.druid.query.filter.AndDimFilter) OrDimFilter(io.druid.query.filter.OrDimFilter) ArrayList(java.util.ArrayList) List(java.util.List) ISE(io.druid.java.util.common.ISE) OrDimFilter(io.druid.query.filter.OrDimFilter) DimFilter(io.druid.query.filter.DimFilter) NotDimFilter(io.druid.query.filter.NotDimFilter) AndDimFilter(io.druid.query.filter.AndDimFilter) BoundDimFilter(io.druid.query.filter.BoundDimFilter) Map(java.util.Map)

Aggregations

ISE (io.druid.java.util.common.ISE)158 IOException (java.io.IOException)37 Map (java.util.Map)23 Test (org.junit.Test)21 File (java.io.File)20 List (java.util.List)19 DateTime (org.joda.time.DateTime)18 ArrayList (java.util.ArrayList)17 DataSegment (io.druid.timeline.DataSegment)15 Interval (org.joda.time.Interval)15 Function (com.google.common.base.Function)14 TimeoutException (java.util.concurrent.TimeoutException)12 IAE (io.druid.java.util.common.IAE)10 HashMap (java.util.HashMap)10 ExecutionException (java.util.concurrent.ExecutionException)10 Stopwatch (com.google.common.base.Stopwatch)9 DimensionSpec (io.druid.query.dimension.DimensionSpec)9 ImmutableMap (com.google.common.collect.ImmutableMap)8 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)8 AggregatorFactory (io.druid.query.aggregation.AggregatorFactory)8