Search in sources :

Example 61 with CallExpression

use of com.facebook.presto.spi.relation.CallExpression in project presto by prestodb.

the class PinotFilterExpressionConverter method handleTimeValueCast.

private Optional<String> handleTimeValueCast(Function<VariableReferenceExpression, Selection> context, RowExpression timeFieldExpression, RowExpression timeValueExpression) {
    // Handle the binary comparison logic of <DATE/TIMESTAMP field> <binary op> <DATE/TIMESTAMP literal>.
    // Pinot stores time as:
    // - `DATE`: Stored as `INT`/`LONG` `daysSinceEpoch` value
    // - `TIMESTAMP`: Stored as `LONG` `millisSinceEpoch` value.
    // In order to push down this predicate, we need to convert the literal to the type of Pinot time field.
    // Below code compares the time type of both side:
    // - if same, then directly push down.
    // - if not same, then convert the literal time type to the field time type.
    // - if not compatible time types, returns Optional.empty(), indicates no change has been made in this cast.
    // Take an example of comparing a `DATE` field to a `TIMESTAMP` literal:
    // - Sample predicate: `WHERE eventDate < current_time`.
    // - Input type is the `eventDate` field data type, which is `DATE`.
    // - Expect type is the right side `literal type`, which means right side is `TIMESTAMP`.
    // The code below converts `current_time` from `millisSinceEpoch` value to `daysSinceEpoch` value, which is
    // comparable to values in `eventDate` column.
    Type inputType;
    Type expectedType;
    if (!isDateTimeConstantType(timeFieldExpression.getType()) || !isDateTimeConstantType(timeValueExpression.getType())) {
        return Optional.empty();
    }
    String timeValueString = timeValueExpression.accept(this, context).getDefinition();
    if (timeFieldExpression instanceof CallExpression) {
        // Handles cases like: `cast(eventDate as TIMESTAMP) <  DATE '2014-01-31'`
        // For cast function,
        // - inputType is the argument type,
        // - expectedType is the cast function return type.
        CallExpression callExpression = (CallExpression) timeFieldExpression;
        if (!standardFunctionResolution.isCastFunction(callExpression.getFunctionHandle())) {
            return Optional.empty();
        }
        if (callExpression.getArguments().size() != 1) {
            return Optional.empty();
        }
        inputType = callExpression.getArguments().get(0).getType();
        expectedType = callExpression.getType();
    } else if (timeFieldExpression instanceof VariableReferenceExpression) {
        // For VariableReferenceExpression,
        // Handles queries like: `eventDate <  TIMESTAMP '2014-01-31 00:00:00 UTC'`
        // - inputType is timeFieldExpression type,
        // - expectedType is the timeValueExpression type.
        inputType = timeFieldExpression.getType();
        expectedType = timeValueExpression.getType();
    } else if (timeFieldExpression instanceof ConstantExpression) {
        // timeFieldExpression is a ConstantExpression, directly return.
        return Optional.of(timeValueString);
    } else {
        return Optional.empty();
    }
    if (inputType == DateType.DATE && (expectedType == TimestampType.TIMESTAMP || expectedType == TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE)) {
        // time field is `DATE`, try to convert time value from `TIMESTAMP` to `DATE`
        try {
            return Optional.of(Long.toString(TimeUnit.MILLISECONDS.toDays(Long.parseLong(timeValueString))));
        } catch (NumberFormatException e) {
            throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), format("Unable to parse timestamp string: '%s'", timeValueString), e);
        }
    }
    if ((inputType == TimestampType.TIMESTAMP || inputType == TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE) && expectedType == DateType.DATE) {
        // time field is `TIMESTAMP`, try to convert time value from `DATE` to `TIMESTAMP`
        try {
            return Optional.of(Long.toString(TimeUnit.DAYS.toMillis(Long.parseLong(timeValueString))));
        } catch (NumberFormatException e) {
            throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), format("Unable to parse date string: '%s'", timeValueString), e);
        }
    }
    // Vacuous cast from variable to same type: cast(eventDate, DAYS). Already handled by handleCast.
    return Optional.of(timeValueString);
}
Also used : PinotException(com.facebook.presto.pinot.PinotException) IntegerType(com.facebook.presto.common.type.IntegerType) Type(com.facebook.presto.common.type.Type) BigintType(com.facebook.presto.common.type.BigintType) VarcharType(com.facebook.presto.common.type.VarcharType) OperatorType(com.facebook.presto.common.function.OperatorType) TimestampWithTimeZoneType(com.facebook.presto.common.type.TimestampWithTimeZoneType) DateType(com.facebook.presto.common.type.DateType) TimestampType(com.facebook.presto.common.type.TimestampType) VariableReferenceExpression(com.facebook.presto.spi.relation.VariableReferenceExpression) ConstantExpression(com.facebook.presto.spi.relation.ConstantExpression) PinotPushdownUtils.getLiteralAsString(com.facebook.presto.pinot.PinotPushdownUtils.getLiteralAsString) CallExpression(com.facebook.presto.spi.relation.CallExpression)

Example 62 with CallExpression

use of com.facebook.presto.spi.relation.CallExpression in project presto by prestodb.

the class PinotPushdownUtils method handlePushDownSingleDistinctCount.

/**
 * Try to push down query like: `SELECT count(distinct $COLUMN) FROM myTable` to Pinot as `SELECT distinctCount($COLUMN) FROM myTable`.
 * This function only handles the case of an AggregationNode (COUNT on $COLUMN) on top of an AggregationNode(of non-aggregate on $COLUMN).
 *
 * @param nodeBuilder
 * @param aggregationNode
 * @param outputColumn
 * @param aggregation
 * @return true if push down successfully otherwise false.
 */
private static boolean handlePushDownSingleDistinctCount(ImmutableList.Builder<AggregationColumnNode> nodeBuilder, AggregationNode aggregationNode, VariableReferenceExpression outputColumn, AggregationNode.Aggregation aggregation) {
    if (!aggregation.getCall().getDisplayName().equalsIgnoreCase(COUNT_FUNCTION_NAME)) {
        return false;
    }
    List<RowExpression> arguments = aggregation.getCall().getArguments();
    if (arguments.size() != 1) {
        return false;
    }
    RowExpression aggregationArgument = arguments.get(0);
    // Handle the case of Count Aggregation on top of a Non-Agg GroupBy Aggregation.
    if (!(aggregationNode.getSource() instanceof AggregationNode)) {
        return false;
    }
    AggregationNode sourceAggregationNode = (AggregationNode) aggregationNode.getSource();
    Set<String> sourceAggregationGroupSet = getGroupKeys(sourceAggregationNode.getGroupingKeys());
    Set<String> aggregationGroupSet = getGroupKeys(aggregationNode.getGroupingKeys());
    aggregationGroupSet.add(aggregationArgument.toString());
    if (!sourceAggregationGroupSet.containsAll(aggregationGroupSet) && aggregationGroupSet.containsAll(sourceAggregationGroupSet)) {
        return false;
    }
    nodeBuilder.add(new AggregationFunctionColumnNode(outputColumn, new CallExpression(aggregation.getCall().getSourceLocation(), PINOT_DISTINCT_COUNT_FUNCTION_NAME, aggregation.getFunctionHandle(), aggregation.getCall().getType(), ImmutableList.of(aggregationArgument))));
    return true;
}
Also used : RowExpression(com.facebook.presto.spi.relation.RowExpression) AggregationNode(com.facebook.presto.spi.plan.AggregationNode) CallExpression(com.facebook.presto.spi.relation.CallExpression)

Example 63 with CallExpression

use of com.facebook.presto.spi.relation.CallExpression in project presto by prestodb.

the class PinotAggregationProjectConverter method handleDateTruncationViaDateTruncation.

private PinotExpression handleDateTruncationViaDateTruncation(CallExpression function, Map<VariableReferenceExpression, PinotQueryGeneratorContext.Selection> context) {
    RowExpression timeInputParameter = function.getArguments().get(1);
    String inputColumn;
    String inputTimeZone;
    String inputFormat;
    CallExpression timeConversion = getExpressionAsFunction(timeInputParameter, timeInputParameter);
    switch(timeConversion.getDisplayName().toLowerCase(ENGLISH)) {
        case FROM_UNIXTIME:
            inputColumn = timeConversion.getArguments().get(0).accept(this, context).getDefinition();
            inputTimeZone = timeConversion.getArguments().size() > 1 ? getStringFromConstant(timeConversion.getArguments().get(1)) : DateTimeZone.UTC.getID();
            inputFormat = "seconds";
            break;
        default:
            throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), "not supported: " + timeConversion.getDisplayName());
    }
    RowExpression intervalParameter = function.getArguments().get(0);
    if (!(intervalParameter instanceof ConstantExpression)) {
        throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), "interval unit in date_trunc is not supported: " + intervalParameter);
    }
    return derived("dateTrunc(" + inputColumn + "," + inputFormat + ", " + inputTimeZone + ", " + getStringFromConstant(intervalParameter) + ")");
}
Also used : PinotException(com.facebook.presto.pinot.PinotException) ConstantExpression(com.facebook.presto.spi.relation.ConstantExpression) RowExpression(com.facebook.presto.spi.relation.RowExpression) PinotPushdownUtils.getLiteralAsString(com.facebook.presto.pinot.PinotPushdownUtils.getLiteralAsString) CallExpression(com.facebook.presto.spi.relation.CallExpression)

Example 64 with CallExpression

use of com.facebook.presto.spi.relation.CallExpression in project presto by prestodb.

the class PinotAggregationProjectConverter method handleDateTruncationViaDateTimeConvert.

private PinotExpression handleDateTruncationViaDateTimeConvert(CallExpression function, Map<VariableReferenceExpression, PinotQueryGeneratorContext.Selection> context) {
    // Convert SQL standard function `DATE_TRUNC(INTERVAL, DATE/TIMESTAMP COLUMN)` to
    // Pinot's equivalent function `dateTimeConvert(columnName, inputFormat, outputFormat, outputGranularity)`
    // Pinot doesn't have a DATE/TIMESTAMP type. That means the input column (second argument) has been converted from numeric type to DATE/TIMESTAMP using one of the
    // conversion functions in SQL. First step is find the function and find its input column units (seconds, secondsSinceEpoch etc.)
    RowExpression timeInputParameter = function.getArguments().get(1);
    String inputColumn;
    String inputFormat;
    CallExpression timeConversion = getExpressionAsFunction(timeInputParameter, timeInputParameter);
    switch(timeConversion.getDisplayName().toLowerCase(ENGLISH)) {
        case FROM_UNIXTIME:
            inputColumn = timeConversion.getArguments().get(0).accept(this, context).getDefinition();
            inputFormat = "'1:SECONDS:EPOCH'";
            break;
        default:
            throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), "not supported: " + timeConversion.getDisplayName());
    }
    String outputFormat = "'1:MILLISECONDS:EPOCH'";
    String outputGranularity;
    RowExpression intervalParameter = function.getArguments().get(0);
    if (!(intervalParameter instanceof ConstantExpression)) {
        throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), "interval unit in date_trunc is not supported: " + intervalParameter);
    }
    String value = getStringFromConstant(intervalParameter);
    switch(value) {
        case "second":
            outputGranularity = "'1:SECONDS'";
            break;
        case "minute":
            outputGranularity = "'1:MINUTES'";
            break;
        case "hour":
            outputGranularity = "'1:HOURS'";
            break;
        case "day":
            outputGranularity = "'1:DAYS'";
            break;
        case "week":
            outputGranularity = "'1:WEEKS'";
            break;
        case "month":
            outputGranularity = "'1:MONTHS'";
            break;
        case "quarter":
            outputGranularity = "'1:QUARTERS'";
            break;
        case "year":
            outputGranularity = "'1:YEARS'";
            break;
        default:
            throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), "interval in date_trunc is not supported: " + value);
    }
    return derived("dateTimeConvert(" + inputColumn + ", " + inputFormat + ", " + outputFormat + ", " + outputGranularity + ")");
}
Also used : PinotException(com.facebook.presto.pinot.PinotException) ConstantExpression(com.facebook.presto.spi.relation.ConstantExpression) RowExpression(com.facebook.presto.spi.relation.RowExpression) PinotPushdownUtils.getLiteralAsString(com.facebook.presto.pinot.PinotPushdownUtils.getLiteralAsString) CallExpression(com.facebook.presto.spi.relation.CallExpression)

Aggregations

CallExpression (com.facebook.presto.spi.relation.CallExpression)64 RowExpression (com.facebook.presto.spi.relation.RowExpression)33 VariableReferenceExpression (com.facebook.presto.spi.relation.VariableReferenceExpression)33 Test (org.testng.annotations.Test)22 AggregationNode (com.facebook.presto.spi.plan.AggregationNode)20 FunctionHandle (com.facebook.presto.spi.function.FunctionHandle)19 ImmutableList (com.google.common.collect.ImmutableList)18 FunctionAndTypeManager (com.facebook.presto.metadata.FunctionAndTypeManager)16 Type (com.facebook.presto.common.type.Type)14 Map (java.util.Map)14 ConstantExpression (com.facebook.presto.spi.relation.ConstantExpression)13 ImmutableMap (com.google.common.collect.ImmutableMap)13 Optional (java.util.Optional)12 ImmutableList.toImmutableList (com.google.common.collect.ImmutableList.toImmutableList)11 OperatorType (com.facebook.presto.common.function.OperatorType)10 Aggregation (com.facebook.presto.spi.plan.AggregationNode.Aggregation)10 PlanNode (com.facebook.presto.spi.plan.PlanNode)10 ProjectNode (com.facebook.presto.spi.plan.ProjectNode)10 SpecialFormExpression (com.facebook.presto.spi.relation.SpecialFormExpression)10 Page (com.facebook.presto.common.Page)8