/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.drill.exec.expr.stat; import com.google.common.base.Preconditions; import org.apache.drill.common.exceptions.DrillRuntimeException; import org.apache.drill.common.expression.FunctionHolderExpression; import org.apache.drill.common.expression.LogicalExpression; import org.apache.drill.common.expression.SchemaPath; import org.apache.drill.common.expression.ValueExpressions; import org.apache.drill.common.expression.fn.CastFunctions; import org.apache.drill.common.expression.fn.FuncHolder; import org.apache.drill.common.expression.visitors.AbstractExprVisitor; import org.apache.drill.common.types.TypeProtos; import org.apache.drill.common.types.Types; import org.apache.drill.exec.expr.DrillSimpleFunc; import org.apache.drill.exec.expr.fn.DrillSimpleFuncHolder; import org.apache.drill.exec.expr.fn.interpreter.InterpreterEvaluator; import org.apache.drill.exec.expr.holders.BigIntHolder; import org.apache.drill.exec.expr.holders.Float4Holder; import org.apache.drill.exec.expr.holders.Float8Holder; import org.apache.drill.exec.expr.holders.IntHolder; import org.apache.drill.exec.expr.holders.ValueHolder; import org.apache.drill.exec.store.parquet.stat.ColumnStatistics; import org.apache.drill.exec.vector.ValueHolderHelper; import org.apache.parquet.column.statistics.DoubleStatistics; import org.apache.parquet.column.statistics.FloatStatistics; import org.apache.parquet.column.statistics.IntStatistics; import org.apache.parquet.column.statistics.LongStatistics; import org.apache.parquet.column.statistics.Statistics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class RangeExprEvaluator extends AbstractExprVisitor<Statistics, Void, RuntimeException> { static final Logger logger = LoggerFactory.getLogger(RangeExprEvaluator.class); private final Map<SchemaPath, ColumnStatistics> columnStatMap; private final long rowCount; public RangeExprEvaluator(final Map<SchemaPath, ColumnStatistics> columnStatMap, long rowCount) { this.columnStatMap = columnStatMap; this.rowCount = rowCount; } public long getRowCount() { return this.rowCount; } @Override public Statistics visitUnknown(LogicalExpression e, Void value) throws RuntimeException { if (e instanceof TypedFieldExpr) { TypedFieldExpr fieldExpr = (TypedFieldExpr) e; final ColumnStatistics columnStatistics = columnStatMap.get(fieldExpr.getPath()); if (columnStatistics != null) { return columnStatistics.getStatistics(); } else { // field does not exist. Preconditions.checkArgument(fieldExpr.getMajorType().equals(Types.OPTIONAL_INT)); IntStatistics intStatistics = new IntStatistics(); intStatistics.setNumNulls(rowCount); // all values are nulls return intStatistics; } } return null; } @Override public Statistics visitIntConstant(ValueExpressions.IntExpression expr, Void value) throws RuntimeException { return getStatistics(expr.getInt()); } @Override public Statistics visitLongConstant(ValueExpressions.LongExpression expr, Void value) throws RuntimeException { return getStatistics(expr.getLong()); } @Override public Statistics visitFloatConstant(ValueExpressions.FloatExpression expr, Void value) throws RuntimeException { return getStatistics(expr.getFloat()); } @Override public Statistics visitDoubleConstant(ValueExpressions.DoubleExpression expr, Void value) throws RuntimeException { return getStatistics(expr.getDouble()); } @Override public Statistics visitDateConstant(ValueExpressions.DateExpression expr, Void value) throws RuntimeException { long dateInMillis = expr.getDate(); return getStatistics(dateInMillis); } @Override public Statistics visitTimeStampConstant(ValueExpressions.TimeStampExpression tsExpr, Void value) throws RuntimeException { long tsInMillis = tsExpr.getTimeStamp(); return getStatistics(tsInMillis); } @Override public Statistics visitTimeConstant(ValueExpressions.TimeExpression timeExpr, Void value) throws RuntimeException { int milliSeconds = timeExpr.getTime(); return getStatistics(milliSeconds); } @Override public Statistics visitFunctionHolderExpression(FunctionHolderExpression holderExpr, Void value) throws RuntimeException { FuncHolder funcHolder = holderExpr.getHolder(); if (! (funcHolder instanceof DrillSimpleFuncHolder)) { // Only Drill function is allowed. return null; } final String funcName = ((DrillSimpleFuncHolder) funcHolder).getRegisteredNames()[0]; if (CastFunctions.isCastFunction(funcName)) { Statistics stat = holderExpr.args.get(0).accept(this, null); if (stat != null && ! stat.isEmpty()) { return evalCastFunc(holderExpr, stat); } } return null; } private IntStatistics getStatistics(int value) { return getStatistics(value, value); } private IntStatistics getStatistics(int min, int max) { final IntStatistics intStatistics = new IntStatistics(); intStatistics.setMinMax(min, max); return intStatistics; } private LongStatistics getStatistics(long value) { return getStatistics(value, value); } private LongStatistics getStatistics(long min, long max) { final LongStatistics longStatistics = new LongStatistics(); longStatistics.setMinMax(min, max); return longStatistics; } private DoubleStatistics getStatistics(double value) { return getStatistics(value, value); } private DoubleStatistics getStatistics(double min, double max) { final DoubleStatistics doubleStatistics = new DoubleStatistics(); doubleStatistics.setMinMax(min, max); return doubleStatistics; } private FloatStatistics getStatistics(float value) { return getStatistics(value, value); } private FloatStatistics getStatistics(float min, float max) { final FloatStatistics floatStatistics = new FloatStatistics(); floatStatistics.setMinMax(min, max); return floatStatistics; } private Statistics evalCastFunc(FunctionHolderExpression holderExpr, Statistics input) { try { DrillSimpleFuncHolder funcHolder = (DrillSimpleFuncHolder) holderExpr.getHolder(); DrillSimpleFunc interpreter = funcHolder.createInterpreter(); final ValueHolder minHolder, maxHolder; TypeProtos.MinorType srcType = holderExpr.args.get(0).getMajorType().getMinorType(); TypeProtos.MinorType destType = holderExpr.getMajorType().getMinorType(); if (srcType.equals(destType)) { // same type cast ==> NoOp. return input; } else if (!CAST_FUNC.containsKey(srcType) || !CAST_FUNC.get(srcType).contains(destType)) { return null; // cast func between srcType and destType is NOT allowed. } switch (srcType) { case INT : minHolder = ValueHolderHelper.getIntHolder(((IntStatistics)input).getMin()); maxHolder = ValueHolderHelper.getIntHolder(((IntStatistics)input).getMax()); break; case BIGINT: minHolder = ValueHolderHelper.getBigIntHolder(((LongStatistics)input).getMin()); maxHolder = ValueHolderHelper.getBigIntHolder(((LongStatistics)input).getMax()); break; case FLOAT4: minHolder = ValueHolderHelper.getFloat4Holder(((FloatStatistics)input).getMin()); maxHolder = ValueHolderHelper.getFloat4Holder(((FloatStatistics)input).getMax()); break; case FLOAT8: minHolder = ValueHolderHelper.getFloat8Holder(((DoubleStatistics)input).getMin()); maxHolder = ValueHolderHelper.getFloat8Holder(((DoubleStatistics)input).getMax()); break; default: return null; } final ValueHolder[] args1 = {minHolder}; final ValueHolder[] args2 = {maxHolder}; final ValueHolder minFuncHolder = InterpreterEvaluator.evaluateFunction(interpreter, args1, holderExpr.getName()); final ValueHolder maxFuncHolder = InterpreterEvaluator.evaluateFunction(interpreter, args2, holderExpr.getName()); switch (destType) { //TODO : need handle # of nulls. case INT: return getStatistics( ((IntHolder)minFuncHolder).value, ((IntHolder)maxFuncHolder).value); case BIGINT: return getStatistics( ((BigIntHolder)minFuncHolder).value, ((BigIntHolder)maxFuncHolder).value); case FLOAT4: return getStatistics( ((Float4Holder)minFuncHolder).value, ((Float4Holder)maxFuncHolder).value); case FLOAT8: return getStatistics( ((Float8Holder)minFuncHolder).value, ((Float8Holder)maxFuncHolder).value); default: return null; } } catch (Exception e) { throw new DrillRuntimeException("Error in evaluating function of " + holderExpr.getName() ); } } static Map<TypeProtos.MinorType, Set<TypeProtos.MinorType>> CAST_FUNC = new HashMap<>(); static { // float -> double , int, bigint CAST_FUNC.put(TypeProtos.MinorType.FLOAT4, new HashSet<TypeProtos.MinorType>()); CAST_FUNC.get(TypeProtos.MinorType.FLOAT4).add(TypeProtos.MinorType.FLOAT8); CAST_FUNC.get(TypeProtos.MinorType.FLOAT4).add(TypeProtos.MinorType.INT); CAST_FUNC.get(TypeProtos.MinorType.FLOAT4).add(TypeProtos.MinorType.BIGINT); // double -> float, int, bigint CAST_FUNC.put(TypeProtos.MinorType.FLOAT8, new HashSet<TypeProtos.MinorType>()); CAST_FUNC.get(TypeProtos.MinorType.FLOAT8).add(TypeProtos.MinorType.FLOAT4); CAST_FUNC.get(TypeProtos.MinorType.FLOAT8).add(TypeProtos.MinorType.INT); CAST_FUNC.get(TypeProtos.MinorType.FLOAT8).add(TypeProtos.MinorType.BIGINT); // int -> float, double, bigint CAST_FUNC.put(TypeProtos.MinorType.INT, new HashSet<TypeProtos.MinorType>()); CAST_FUNC.get(TypeProtos.MinorType.INT).add(TypeProtos.MinorType.FLOAT4); CAST_FUNC.get(TypeProtos.MinorType.INT).add(TypeProtos.MinorType.FLOAT8); CAST_FUNC.get(TypeProtos.MinorType.INT).add(TypeProtos.MinorType.BIGINT); // bigint -> int, float, double CAST_FUNC.put(TypeProtos.MinorType.BIGINT, new HashSet<TypeProtos.MinorType>()); CAST_FUNC.get(TypeProtos.MinorType.BIGINT).add(TypeProtos.MinorType.INT); CAST_FUNC.get(TypeProtos.MinorType.BIGINT).add(TypeProtos.MinorType.FLOAT4); CAST_FUNC.get(TypeProtos.MinorType.BIGINT).add(TypeProtos.MinorType.FLOAT8); } }