prestodb/presto · warning · PrestoException
DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION
DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION
Error message
Expected string literal but found: ${expression} to pushdown for Druid connector. What it means
DruidAggregationProjectConverter.getStringFromConstant converts a constant RowExpression into a Java string so it can be pushed down into Druid SQL functions (e.g. as a date unit for DATE_TRUNC). Only VARCHAR literals (String or Slice) are supported; any other constant kind causes a PrestoException with DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION.
Source
Thrown at presto-druid/src/main/java/com/facebook/presto/druid/DruidAggregationProjectConverter.java:168
{
if (function.getDisplayName().toLowerCase(ENGLISH).equals(DATE_TRUNC)) {
return handleDateTruncationViaDateTruncation(function, context);
}
throw new PrestoException(DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION, "Unsupported function: " + function.getDisplayName() + " to pushdown for Druid connector.");
}
private static String getStringFromConstant(RowExpression expression)
{
if (expression instanceof ConstantExpression) {
Object value = ((ConstantExpression) expression).getValue();
if (value instanceof String) {
return (String) value;
}
if (value instanceof Slice) {
return ((Slice) value).toStringUtf8();
}
}
throw new PrestoException(DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION, "Expected string literal but found: " + expression + " to pushdown for Druid connector.");
}
private CallExpression getExpressionAsFunction(
RowExpression originalExpression,
RowExpression expression)
{
if (expression instanceof CallExpression) {
CallExpression call = (CallExpression) expression;
if (standardFunctionResolution.isCastFunction(call.getFunctionHandle())) {
if (isImplicitCast(call.getArguments().get(0).getType(), call.getType())) {
return getExpressionAsFunction(originalExpression, call.getArguments().get(0));
}
}
else {
return call;
}
}
throw new PrestoException(DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION, "Could not dig function out of expression: " + originalExpression + ", inside of: " + expression + " to pushdown for Druid connector.");View on GitHub (pinned to 55bb57d202)
Solutions
- Rewrite the query so the string argument is a plain single-quoted literal, e.g. date_trunc('day', ts) instead of date_trunc(unit_col, ts)
- Remove implicit/extra casts around the literal (CAST('day' AS varchar) should be fine, but non-VARCHAR constants must be replaced)
- If the unit is computed at runtime, compute it in SQL CASE logic instead of relying on pushdown, or run the aggregation without Druid pushdown
- Check the EXPLAIN plan to confirm which expression fails pushdown and simplify it
Example fix
-- before
SELECT date_trunc(unit, ts), count(*) FROM druid_table GROUP BY 1; -- non-literal unit
-- after
SELECT date_trunc('day', ts), count(*) FROM druid_table GROUP BY 1; Defensive patterns
Strategy: validation
Validate before calling
// before relying on Druid pushdown for date_trunc, check the unit argument is a plain literal
RowExpression unitArg = getFirstArgument(callExpression);
boolean pushable = unitArg instanceof ConstantExpression
&& ((ConstantExpression) unitArg).getValue() instanceof String;
if (!pushable) { /* plan without pushdown or rewrite the query */ } Type guard
boolean isStringLiteral(RowExpression e) {
return e instanceof ConstantExpression
&& (((ConstantExpression) e).getValue() instanceof String
|| ((ConstantExpression) e).getValue() instanceof Slice);
} Try / catch
try {
String unit = converter.getStringFromConstant(expression);
} catch (PrestoException e) {
if (e.getCode().equals(DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION)) {
// fall back: evaluate the aggregation without Druid pushdown
return evaluateLocally(expression);
}
throw e;
} Prevention
- Always pass single-quoted string literals (e.g. 'day') to date_trunc in queries targeting Druid pushdown
- Avoid bind parameters, CASE expressions, or computed values as the unit argument when pushdown matters
- Check EXPLAIN / connector logs to confirm the aggregation was actually pushed down
- Keep the timestamp argument as a supported function call so the converter can dig out the call
When it happens
Trigger: handleDateTruncationViaDateTruncation pushes a DATE_TRUNC-like aggregation whose unit argument (or other string argument) is a constant expression that is not a VARCHAR literal — e.g. a non-literal expression, a cast, or a numeric/timestamp constant — reaching getStringFromConstant and failing the value instanceof String/Slice checks.
Common situations: Queries like date_trunc('day', ts) where the unit comes from a bind parameter, a CASE expression, or is written without quotes; queries generated by ORMs/bi tools emitting casts around the literal; pushing down functions with arguments Druid cannot receive as plain strings.
Related errors
- DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION
- DRUID_QUERY_GENERATOR_FAILURE
- DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION
- DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION
- DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/98b806a4b6542cf4.
Report an issue: GitHub.