use of com.facebook.presto.pinot.PinotException in project presto by prestodb.
the class PinotAggregationProjectConverter method visitCall.
@Override
public PinotExpression visitCall(CallExpression call, Map<VariableReferenceExpression, PinotQueryGeneratorContext.Selection> context) {
Optional<PinotExpression> basicCallHandlingResult = basicCallHandling(call, context);
if (basicCallHandlingResult.isPresent()) {
return basicCallHandlingResult.get();
}
FunctionMetadata functionMetadata = functionMetadataManager.getFunctionMetadata(call.getFunctionHandle());
Optional<OperatorType> operatorTypeOptional = functionMetadata.getOperatorType();
if (operatorTypeOptional.isPresent()) {
OperatorType operatorType = operatorTypeOptional.get();
if (operatorType.isArithmeticOperator()) {
return handleArithmeticExpression(call, operatorType, context);
}
if (operatorType.isComparisonOperator()) {
throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), "Comparison operator not supported: " + call);
}
}
return handleFunction(call, context);
}
use of com.facebook.presto.pinot.PinotException 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.pinot.PinotException in project presto by prestodb.
the class PinotFilterExpressionConverter method handleContains.
private PinotExpression handleContains(CallExpression contains, Function<VariableReferenceExpression, Selection> context) {
if (contains.getArguments().size() != 2) {
throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), format("Contains operator not supported: %s", contains));
}
RowExpression left = contains.getArguments().get(0);
RowExpression right = contains.getArguments().get(1);
if (!(right instanceof ConstantExpression)) {
throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), format("Contains operator can not push down non-literal value: %s", right));
}
return derived(format("(%s = %s)", left.accept(this, context).getDefinition(), right.accept(this, context).getDefinition()));
}
use of com.facebook.presto.pinot.PinotException in project presto by prestodb.
the class PinotFilterExpressionConverter method visitCall.
@Override
public PinotExpression visitCall(CallExpression call, Function<VariableReferenceExpression, Selection> context) {
FunctionHandle functionHandle = call.getFunctionHandle();
if (standardFunctionResolution.isNotFunction(functionHandle)) {
return handleNot(call, context);
}
if (standardFunctionResolution.isCastFunction(functionHandle)) {
return handleCast(call, context);
}
if (standardFunctionResolution.isBetweenFunction(functionHandle)) {
return handleBetween(call, context);
}
FunctionMetadata functionMetadata = functionMetadataManager.getFunctionMetadata(call.getFunctionHandle());
Optional<OperatorType> operatorTypeOptional = functionMetadata.getOperatorType();
if (operatorTypeOptional.isPresent()) {
OperatorType operatorType = operatorTypeOptional.get();
if (operatorType.isArithmeticOperator()) {
throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), "Arithmetic expressions are not supported in filter: " + call);
}
if (operatorType.isComparisonOperator()) {
return handleLogicalBinary(operatorType.getOperator(), call, context);
}
}
if ("contains".equals(functionMetadata.getName().getObjectName())) {
return handleContains(call, context);
}
// Otherwise TypeManager.canCoerce(...) will return false and directly fail this query.
if (functionMetadata.getName().getObjectName().equalsIgnoreCase("$literal$timestamp") || functionMetadata.getName().getObjectName().equalsIgnoreCase("$literal$date")) {
return handleDateAndTimestampMagicLiteralFunction(call, context);
}
throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.empty(), format("function %s not supported in filter", call));
}
use of com.facebook.presto.pinot.PinotException in project presto by prestodb.
the class PinotQueryGeneratorContext method toSqlQuery.
/**
* Convert the current context to a Pinot SQL
*/
public PinotQueryGenerator.GeneratedPinotQuery toSqlQuery(PinotConfig pinotConfig, ConnectorSession session) {
int nonAggregateShortQueryLimit = PinotSessionProperties.getNonAggregateLimitForBrokerQueries(session);
boolean isQueryShort = (hasAggregation() || hasGroupBy()) || limit.orElse(Integer.MAX_VALUE) < nonAggregateShortQueryLimit;
boolean forBroker = !PinotSessionProperties.isForbidBrokerQueries(session) && isQueryShort;
String groupByExpressions = groupByColumns.stream().map(x -> selections.get(x).getDefinition()).collect(Collectors.joining(", "));
String selectExpressions = outputs.stream().filter(o -> !groupByColumns.contains(o)).map(o -> updateSelection(selections.get(o).getDefinition(), session)).collect(Collectors.joining(", "));
String expressions = (groupByExpressions.isEmpty()) ? selectExpressions : (selectExpressions.isEmpty()) ? groupByExpressions : groupByExpressions + ", " + selectExpressions;
String tableName = from.orElseThrow(() -> new PinotException(PINOT_QUERY_GENERATOR_FAILURE, Optional.empty(), "Table name not encountered yet"));
// Rules for limit:
// - If its a selection query:
// + given limit or configured limit
// - Else if has group by:
// + default limit or configured top limit
// - Aggregation only query limit is ignored.
// - Fail if limit is invalid
int queryLimit = -1;
if (!hasAggregation() && !hasGroupBy()) {
if (!limit.isPresent() && forBroker) {
throw new PinotException(PINOT_QUERY_GENERATOR_FAILURE, Optional.empty(), "Broker non aggregate queries have to have a limit");
} else {
queryLimit = limit.orElse(PinotSessionProperties.getLimitLargerForSegment(session));
}
} else if (hasGroupBy()) {
if (limit.isPresent()) {
queryLimit = limit.getAsInt();
} else {
queryLimit = PinotSessionProperties.getTopNLarge(session);
}
}
String limitClause = "";
if (queryLimit > 0) {
limitClause = " LIMIT " + queryLimit;
}
String query = generatePinotQueryHelper(forBroker, expressions, tableName, limitClause);
LinkedHashMap<VariableReferenceExpression, PinotColumnHandle> assignments = getAssignments(true);
List<Integer> indices = getIndicesMappingFromPinotSchemaToPrestoSchema(query, assignments);
return new PinotQueryGenerator.GeneratedPinotQuery(tableName, query, PinotQueryGenerator.PinotQueryFormat.SQL, indices, groupByColumns.size(), filter.isPresent(), isQueryShort);
}
Aggregations