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

  1. 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)
  2. Remove implicit/extra casts around the literal (CAST('day' AS varchar) should be fine, but non-VARCHAR constants must be replaced)
  3. 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
  4. 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

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


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/98b806a4b6542cf4. Report an issue: GitHub.