prestodb/presto · error · PrestoException

DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION

DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION

Error message

Druid does not support filter on top of AggregationNode.

What it means

Druid's native groupBy/topN query model does not allow an outer filter applied above the aggregation result the way Presto plans it (filter on top of AggregationNode). The context builder refuses to attach a filter once aggregations exist, since it cannot express the plan faithfully in a single Druid query.

Source

Thrown at presto-druid/src/main/java/com/facebook/presto/druid/DruidQueryGeneratorContext.java:115

            Set<VariableReferenceExpression> variablesInAggregation,
            Set<VariableReferenceExpression> hiddenColumnSet,
            Optional<PlanNodeId> tableScanNodeId)
    {
        this.selections = new LinkedHashMap<>(requireNonNull(selections, "selections can't be null"));
        this.from = requireNonNull(from, "source can't be null");
        this.filter = requireNonNull(filter, "filter is null");
        this.limit = requireNonNull(limit, "limit is null");
        this.aggregations = aggregations;
        this.groupByColumns = new LinkedHashMap<>(requireNonNull(groupByColumns, "groupByColumns can't be null. It could be empty if not available"));
        this.hiddenColumnSet = requireNonNull(hiddenColumnSet, "hidden column set is null");
        this.variablesInAggregation = requireNonNull(variablesInAggregation, "variables in aggregation is null");
        this.tableScanNodeId = requireNonNull(tableScanNodeId, "tableScanNodeId can't be null");
    }

    public DruidQueryGeneratorContext withFilter(String filter)
    {
        if (hasAggregation()) {
            throw new PrestoException(DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION, "Druid does not support filter on top of AggregationNode.");
        }
        checkState(!hasFilter(), "Druid doesn't support filters at multiple levels under AggregationNode");
        return new DruidQueryGeneratorContext(
                selections,
                from,
                Optional.of(filter),
                limit,
                aggregations,
                groupByColumns,
                variablesInAggregation,
                hiddenColumnSet,
                tableScanNodeId);
    }

    public DruidQueryGeneratorContext withProject(Map<VariableReferenceExpression, Selection> newSelections)
    {
        return new DruidQueryGeneratorContext(
                newSelections,

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Rewrite as a HAVING clause (SELECT k, sum(v) FROM t GROUP BY k HAVING sum(v) > 10) so the filter folds into the aggregation
  2. Disable pushdown (connector-level or session property) so the filter is evaluated in Presto
  3. Restructure the query to filter base columns before aggregation (push filters below the GROUP BY)
  4. Modify DruidQueryGeneratorContext/visitor to convert qualifying post-aggregation filters into Druid having specs

Example fix

// before
SELECT * FROM (SELECT k, sum(v) s FROM t GROUP BY k) WHERE s > 10; -- throws
// after
SELECT k, sum(v) FROM t GROUP BY k HAVING sum(v) > 10;
Defensive patterns

Strategy: try-catch

Validate before calling

boolean isFilterAboveAggregationSafe(PlanNode filterNode) {
    return !(filterNode.getSource() instanceof AggregationNode);
}

Try / catch

try { return context.withFilter(filterSql); }
catch (PrestoException e) {
    if (DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION.getCode().equals(e.getErrorCode().getCode())) {
        return skipPushdownAndEvaluateFilterInPresto();
    }
    throw e;
}

Prevention

When it happens

Trigger: A query plan reaches visitFilter after aggregation was recorded, e.g. SELECT ... FROM (SELECT k, sum(v) AS s FROM t GROUP BY k) WHERE s > 10 — a HAVING-like/outer filter over an aggregate — and the connector tries to push the filter into the Druid context.

Common situations: Filtering on the result of an aggregate (HAVING with complex expressions) or filtering a derived table built on top of a Druid aggregation; typically surfaces when the planner attempts predicate pushdown past an AggregationNode.

Related errors


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