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);
}
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;
}
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) + ")");
}
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 + ")");
}
Aggregations