{"record":{"id":"91b47e1d16efdb29","repo":"prestodb/presto","slug":"druid-pushdown-unsupported-expression-91b47e","errorCode":"DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION","errorMessage":"Unsupported aggregation node ${aggregationNode}","messagePattern":"Unsupported aggregation node (.+?)","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-druid/src/main/java/com/facebook/presto/druid/DruidPushdownUtils.java","lineNumber":77,"sourceCode":"    public static final String DRUID_COUNT_DISTINCT_FUNCTION_NAME = \"distinctCount\";\n\n    private static final String COUNT_FUNCTION_NAME = \"count\";\n    private static final String DISTINCT_MASK = \"$distinct\";\n\n    private DruidPushdownUtils() {}\n\n    public static List<DruidAggregationColumnNode> computeAggregationNodes(AggregationNode aggregationNode)\n    {\n        int groupByKeyIndex = 0;\n        ImmutableList.Builder<DruidAggregationColumnNode> nodeBuilder = ImmutableList.builder();\n        for (VariableReferenceExpression outputColumn : aggregationNode.getOutputVariables()) {\n            AggregationNode.Aggregation aggregation = aggregationNode.getAggregations().get(outputColumn);\n\n            if (aggregation != null) {\n                if (aggregation.getFilter().isPresent()\n                        || aggregation.isDistinct()\n                        || aggregation.getOrderBy().isPresent()) {\n                    throw new PrestoException(DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION, \"Unsupported aggregation node \" + aggregationNode);\n                }\n                if (aggregation.getMask().isPresent()) {\n                    // This block handles the case when a distinct aggregation is present in addition to another aggregation function.\n                    // E.g. `SELECT count(distinct COL_A), sum(COL_B) FROM myTable` to Druid as `SELECT distinctCount(COL_A), sum(COL_B) FROM myTable`\n                    if (aggregation.getCall().getDisplayName().equalsIgnoreCase(COUNT_FUNCTION_NAME) && aggregation.getMask().get().getName().equalsIgnoreCase(aggregation.getArguments().get(0) + DISTINCT_MASK)) {\n                        nodeBuilder.add(new AggregationFunctionColumnNode(outputColumn, new CallExpression(aggregation.getCall().getSourceLocation(), DRUID_COUNT_DISTINCT_FUNCTION_NAME, aggregation.getCall().getFunctionHandle(), aggregation.getCall().getType(), aggregation.getCall().getArguments())));\n                        continue;\n                    }\n                    // Druid doesn't support push down aggregation functions other than count on top of distinct function.\n                    throw new PrestoException(DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION, \"Unsupported aggregation node with mask \" + aggregationNode);\n                }\n                if (handlePushDownSingleDistinctCount(nodeBuilder, aggregationNode, outputColumn, aggregation)) {\n                    continue;\n                }\n                nodeBuilder.add(new AggregationFunctionColumnNode(outputColumn, aggregation.getCall()));\n            }\n            else {\n                // group by output","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-druid/src/main/java/com/facebook/presto/druid/DruidPushdownUtils.java#L59-L95","documentation":"DruidPushdownUtils.computeAggregationNodes throws this when an AggregationNode contains an aggregation that has a filter, is DISTINCT, or has an ORDER BY — none of which Druid pushdown supports in the general case. Pushdown of that aggregation plan is refused with DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION.","triggerScenarios":"Computing aggregation nodes for pushdown when aggregation.getFilter().isPresent() || aggregation.isDistinct() || aggregation.getOrderBy().isPresent() — e.g. FILTER clauses, generic DISTINCT aggregations, or ORDER BY inside aggregates.","commonSituations":"Queries like SELECT sum(x ORDER BY y), avg(x) FILTER (WHERE z), or count(DISTINCT col) with an unrecognized mask over Druid tables.","solutions":["Remove FILTER/ORDER BY/DISTINCT from aggregate functions in the query.","For count(DISTINCT col), keep the simple form (the connector handles count + standard distinct mask via distinctCount); exotic distinct masks are not supported.","Split the query: push down the simple aggregations to Druid and compute the filtered/ordered aggregates client-side or in a separate non-pushed-down pass.","Rewrite filtered aggregates as CASE-based sums if a newer connector supports CASE pushdown, or upgrade Presto."],"exampleFix":"// before\nSELECT sum(x) FILTER (WHERE y > 0) FROM druid_table;\n// after\nSELECT sum(CASE WHEN y > 0 THEN x END) FROM druid_table; -- or compute client-side","handlingStrategy":"fallback","validationCode":"boolean pushable(AggregationNode agg) {\n    return agg.getAggregations().values().stream().noneMatch(a ->\n        a.getFilter().isPresent() || a.isDistinct() || a.getOrderBy().isPresent());\n}","typeGuard":null,"tryCatchPattern":"try { druidQuery = DruidPushdownUtils.computeAggregationNodes(agg, builder); }\ncatch (PrestoException e) {\n    if (e.getErrorCode().getName().equals(\"DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION\")) { runAggregationInPresto(); }\n    else throw e;\n}","preventionTips":["Avoid FILTER and ORDER BY inside Druid aggregate calls.","Restrict DISTINCT to count(DISTINCT col) over Druid tables.","Split complex aggregates into separate queries.","Verify aggregation plan shape with EXPLAIN."],"tags":["presto","druid","pushdown","aggregation"],"backgroundTag":"druid-pushdown-unsupported-expression","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}