Source:
<code>private GroupByQuery( final DataSource dataSource, final QuerySegmentSpec querySegmentSpec, final VirtualColumns virtualColumns, final DimFilter dimFilter, final Granularity granularity, final List<DimensionSpec> dimensions, final List<AggregatorFactory> aggregatorSpecs, final List<PostAggregator> postAggregatorSpecs, final HavingSpec havingSpec, final LimitSpec limitSpec, final @Nullable Function<Sequence<Row>, Sequence<Row>> postProcessingFn, final Map<String, Object> context ) { super(dataSource, querySegmentSpec, false, context, granularity); this.virtualColumns = VirtualColumns.nullToEmpty(virtualColumns); this.dimFilter = dimFilter; this.dimensions = dimensions == null ? ImmutableList.of() : dimensions; for (DimensionSpec spec : this.dimensions) { Preconditions.checkArgument(spec != null, "dimensions has null DimensionSpec"); } <START> Preconditions.checkNotNull(granularity, "Must specify a granularity"); <END> this.aggregatorSpecs = aggregatorSpecs == null ? ImmutableList.<AggregatorFactory>of() : aggregatorSpecs; this.postAggregatorSpecs = Queries.prepareAggregations( this.dimensions.stream().map(DimensionSpec::getOutputName).collect(Collectors.toList()), this.aggregatorSpecs, postAggregatorSpecs == null ? ImmutableList.<PostAggregator>of() : postAggregatorSpecs ); this.havingSpec = havingSpec; this.limitSpec = LimitSpec.nullToNoopLimitSpec(limitSpec); verifyOutputNames(this.dimensions, this.aggregatorSpecs, this.postAggregatorSpecs); this.postProcessingFn = postProcessingFn != null ? postProcessingFn : makePostProcessingFn(); this.applyLimitPushDown = determineApplyLimitPushDown(); }</code><technical_language>This check done in TimeBucketedQuery's constructor</technical_language>

Target:
private GroupByQuery( final DataSource dataSource, final QuerySegmentSpec querySegmentSpec, final VirtualColumns virtualColumns, final DimFilter dimFilter, final Granularity granularity, final List<DimensionSpec> dimensions, final List<AggregatorFactory> aggregatorSpecs, final List<PostAggregator> postAggregatorSpecs, final HavingSpec havingSpec, final LimitSpec limitSpec, final @Nullable Function<Sequence<Row>, Sequence<Row>> postProcessingFn, final Map<String, Object> context ) { super(dataSource, querySegmentSpec, false, context, granularity); this.virtualColumns = VirtualColumns.nullToEmpty(virtualColumns); this.dimFilter = dimFilter; this.dimensions = dimensions == null ? ImmutableList.of() : dimensions; for (DimensionSpec spec : this.dimensions) { Preconditions.checkArgument(spec != null, "dimensions has null DimensionSpec"); } this.aggregatorSpecs = aggregatorSpecs == null ? ImmutableList.<AggregatorFactory>of() : aggregatorSpecs; this.postAggregatorSpecs = Queries.prepareAggregations( this.dimensions.stream().map(DimensionSpec::getOutputName).collect(Collectors.toList()), this.aggregatorSpecs, postAggregatorSpecs == null ? ImmutableList.<PostAggregator>of() : postAggregatorSpecs ); this.havingSpec = havingSpec; this.limitSpec = LimitSpec.nullToNoopLimitSpec(limitSpec); verifyOutputNames(this.dimensions, this.aggregatorSpecs, this.postAggregatorSpecs); this.postProcessingFn = postProcessingFn != null ? postProcessingFn : makePostProcessingFn(); this.applyLimitPushDown = determineApplyLimitPushDown(); }