{"record":{"id":"373626f173a0899a","repo":"apache/druid","slug":"limit-push-down-when-sorting-by-a-post-aggregator","errorCode":null,"errorMessage":"Limit push down when sorting by a post aggregator is not supported.","messagePattern":"Limit push down when sorting by a post aggregator is not supported\\.","errorType":"validation","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java","lineNumber":519,"sourceCode":"  private boolean validateAndGetForceLimitPushDown()\n  {\n    final boolean forcePushDown = context().getBoolean(GroupByQueryConfig.CTX_KEY_FORCE_LIMIT_PUSH_DOWN, false);\n    if (forcePushDown) {\n      if (!(limitSpec instanceof DefaultLimitSpec)) {\n        throw new IAE(\"When forcing limit push down, a limit spec must be provided.\");\n      }\n\n      if (!((DefaultLimitSpec) limitSpec).isLimited()) {\n        throw new IAE(\"When forcing limit push down, the provided limit spec must have a limit.\");\n      }\n\n      if (havingSpec != null) {\n        throw new IAE(\"Cannot force limit push down when a having spec is present.\");\n      }\n\n      for (OrderByColumnSpec orderBySpec : ((DefaultLimitSpec) limitSpec).getColumns()) {\n        if (OrderByColumnSpec.getPostAggIndexForOrderBy(orderBySpec, postAggregatorSpecs) > -1) {\n          throw new UnsupportedOperationException(\"Limit push down when sorting by a post aggregator is not supported.\");\n        }\n      }\n    }\n    return forcePushDown;\n  }\n\n  private RowSignature computeResultRowSignature(final RowSignature.Finalization finalization)\n  {\n    final RowSignature.Builder builder = RowSignature.builder();\n\n    if (universalTimestamp == null) {\n      builder.addTimeColumn();\n    }\n\n    return builder.addDimensions(dimensions)\n                  .addAggregators(aggregatorSpecs, finalization)\n                  .addPostAggregators(postAggregatorSpecs)\n                  .build();","sourceCodeStart":501,"sourceCodeEnd":537,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java#L501-L537","documentation":"When forcing limit push down, GroupByQuery does not support an orderBy column that refers to a post-aggregator. The sort column must be a dimension or aggregator output; sorting by a post-aggregation value cannot be pushed into the group-by engine, so an UnsupportedOperationException is thrown.","triggerScenarios":"Calling GroupByQuery.isApplyLimitPushDown / validateAndGetForceLimitPushDown with a DefaultLimitSpec whose OrderByColumnSpec matches (via getPostAggIndexForOrderBy) one of the query's postAggregatorSpecs while forcing push down.","commonSituations":"SQL ORDER BY on an expression computed from aggregations translated into a post-aggregator sort with limit push down enabled; hand-built native queries sorting on ratio/computed columns with limits.","solutions":["Remove the post-aggregator from the ORDER BY / limit spec, sorting only by dimensions or aggregators","Convert the post-aggregator expression into an actual aggregator where possible","Disable limit push down (applyLimitPushDown=false) so the limit is applied after post-aggregation"],"exampleFix":"// before\n.limit(new DefaultLimitSpec(ImmutableList.of(new OrderByColumnSpec(\"ratio\", ASC)), 10)) // 'ratio' is a post-agg\n// after\n.limit(new DefaultLimitSpec(ImmutableList.of(new OrderByColumnSpec(\"count\", ASC)), 10)) // sort by aggregator","handlingStrategy":"validation","validationCode":"boolean sortsByPostAgg = ((DefaultLimitSpec) q.getLimitSpec()).getColumns().stream().anyMatch(c -> OrderByColumnSpec.getPostAggIndexForOrderBy(c, q.getPostAggregatorSpecs()) > -1);","typeGuard":"boolean limitColumnsAreNotPostAggs(GroupByQuery q) { return q.getLimitSpec() instanceof DefaultLimitSpec && ((DefaultLimitSpec) q.getLimitSpec()).getColumns().stream().noneMatch(c -> OrderByColumnSpec.getPostAggIndexForOrderBy(c, q.getPostAggregatorSpecs()) > -1); }","tryCatchPattern":"try { validateAndGetForceLimitPushDown(query, forcePushDown); } catch (UnsupportedOperationException e) { return false; }","preventionTips":["Only order by dimension or aggregator output names when using limit push down","Keep post-aggregators out of ORDER BY clauses in push-down-enabled queries"],"tags":["groupby","post-aggregator","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}