prestodb/presto · warning · PrestoException

CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION

CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION

Error message

ClickHouse does not support filter on top of AggregationNode.

What it means

The ClickHouse pushdown optimizer only supports pushing a filter below or beside an aggregation (e.g. HAVING-style is not modeled here); it cannot represent a filter applied on top of an AggregationNode in the generated SQL. When withFilter is invoked on a context that already contains an aggregation, the pushdown is aborted so the query falls back to non-pushed execution. This is a deliberate capability limit of the generator, not a ClickHouse server error.

Source

Thrown at presto-clickhouse/src/main/java/com/facebook/presto/plugin/clickhouse/optimization/ClickHouseQueryGeneratorContext.java:120

            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.schema = requireNonNull(schema, "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 ClickHouseQueryGeneratorContext withFilter(String filter)
    {
        if (hasAggregation()) {
            throw new PrestoException(CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION, "ClickHouse does not support filter on top of AggregationNode.");
        }
        checkState(!hasFilter(), "ClickHouse doesn't support filters at multiple levels under AggregationNode");
        return new ClickHouseQueryGeneratorContext(
                selections,
                from,
                schema,
                Optional.of(filter),
                limit,
                aggregations,
                groupByColumns,
                variablesInAggregation,
                hiddenColumnSet,
                tableScanNodeId);
    }

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

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Rewrite the query so the filter sits below the aggregation (filter raw rows in a WHERE before grouping) rather than above it.
  2. Disable ClickHouse pushdown for such queries so Presto executes the filter locally after the aggregation.
  3. If HAVING is needed, express it in a form the connector supports, or upgrade the connector — check whether your version added HAVING pushdown support.
  4. File/patch the connector to translate top-of-aggregation filters into HAVING clauses.

Example fix

// before (filter above aggregation)
SELECT k, count(*) FROM t GROUP BY k HAVING count(*) > 5;
// after (filter below aggregation where possible)
SELECT k, count(*) FROM t WHERE valid = true GROUP BY k;
Defensive patterns

Strategy: validation

Validate before calling

// Detect filter-on-top-of-aggregation shape before expecting pushdown
boolean filterAboveAggregation = planContains(FilterNode.class, node ->
    childOf(node, AggregationNode.class) && beneathAggregationIsClickHouseScan(node));
// if true, don't rely on pushdown; rewrite or run locally

Try / catch

try {
    execute(query);
} catch (PrestoException e) {
    if ("CLICKHOUSE_PUSHDOWN_UNSUPPORTED_EXPRESSION".equals(e.getErrorCode().getName())) {
        executeWithoutPushdown(query); // session-level pushdown disabled fallback
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: visitFilter calls withFilter(String) while the ClickHouseQueryGeneratorContext already hasAggregation() == true — i.e. the plan has FilterNode -> AggregationNode -> TableScan (or filter otherwise lands above the aggregation during pushdown).

Common situations: Queries like SELECT ... FROM t GROUP BY k HAVING agg ... where the planner builds a filter node on top of the aggregation, or views/subqueries with WHERE clauses wrapped around a GROUP BY, when ClickHouse pushdown is enabled.

Related errors


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