Construct aggregation analysis result if query tree has GROUP BY or aggregates. * Actions before aggregation are added into actions chain, if result is not null optional. */
| 129 | * Actions before aggregation are added into actions chain, if result is not null optional. |
| 130 | */ |
| 131 | std::optional<AggregationAnalysisResult> analyzeAggregation( |
| 132 | const QueryTreeNodePtr & query_tree, |
| 133 | const ColumnsWithTypeAndName & input_columns, |
| 134 | const PlannerContextPtr & planner_context, |
| 135 | const ColumnNodePtrWithHashSet & correlated_columns_set, |
| 136 | ActionsChain & actions_chain) |
| 137 | { |
| 138 | auto & query_node = query_tree->as<QueryNode &>(); |
| 139 | |
| 140 | auto aggregate_function_nodes = collectAggregateFunctionNodes(query_tree); |
| 141 | auto aggregates_descriptions = extractAggregateDescriptions(aggregate_function_nodes, *planner_context); |
| 142 | |
| 143 | ColumnsWithTypeAndName available_columns_after_aggregation; |
| 144 | available_columns_after_aggregation.reserve(aggregates_descriptions.size()); |
| 145 | for (auto & aggregate_description : aggregates_descriptions) |
| 146 | available_columns_after_aggregation.emplace_back(nullptr, aggregate_description.function->getResultType(), aggregate_description.column_name); |
| 147 | |
| 148 | Names aggregation_keys; |
| 149 | |
| 150 | ActionsAndProjectInputsFlagPtr before_aggregation_actions = std::make_shared<ActionsAndProjectInputsFlag>(); |
| 151 | /// Here it is OK to materialize const columns: if column is used in GROUP BY, it may be expected to become non-const |
| 152 | /// See https://github.com/ClickHouse/ClickHouse/issues/70655 for example |
| 153 | before_aggregation_actions->dag = ActionsDAG(input_columns, false); |
| 154 | before_aggregation_actions->dag.getOutputs().clear(); |
| 155 | |
| 156 | std::unordered_set<std::string_view> before_aggregation_actions_output_node_names; |
| 157 | |
| 158 | GroupingSetsParamsList grouping_sets_parameters_list; |
| 159 | bool group_by_with_constant_keys = false; |
| 160 | |
| 161 | PlannerActionsVisitor actions_visitor(planner_context, correlated_columns_set); |
| 162 | |
| 163 | /// Add expressions from GROUP BY |
| 164 | bool group_by_use_nulls = planner_context->getQueryContext()->getSettingsRef()[Setting::group_by_use_nulls] |
| 165 | && (query_node.isGroupByWithGroupingSets() || query_node.isGroupByWithRollup() || query_node.isGroupByWithCube()); |
| 166 | |
| 167 | bool is_secondary_query = planner_context->getQueryContext()->getClientInfo().query_kind == ClientInfo::QueryKind::SECONDARY_QUERY; |
| 168 | bool is_distributed_query = planner_context->getQueryContext()->isDistributed(); |
| 169 | bool check_constants_for_group_by_key = is_secondary_query || is_distributed_query; |
| 170 | |
| 171 | if (query_node.hasGroupBy()) |
| 172 | { |
| 173 | if (query_node.isGroupByWithGroupingSets()) |
| 174 | { |
| 175 | for (auto & grouping_set_keys_list_node : query_node.getGroupBy().getNodes()) |
| 176 | { |
| 177 | auto & grouping_set_keys_list_node_typed = grouping_set_keys_list_node->as<ListNode &>(); |
| 178 | grouping_sets_parameters_list.emplace_back(); |
| 179 | auto & grouping_sets_parameters = grouping_sets_parameters_list.back(); |
| 180 | |
| 181 | for (auto & grouping_set_key_node : grouping_set_keys_list_node_typed.getNodes()) |
| 182 | { |
| 183 | const auto * constant_key = grouping_set_key_node->as<ConstantNode>(); |
| 184 | group_by_with_constant_keys |= (constant_key != nullptr); |
| 185 | |
| 186 | if (constant_key && !aggregates_descriptions.empty() && (!check_constants_for_group_by_key || canRemoveConstantFromGroupByKey(*constant_key))) |
| 187 | continue; |
| 188 |
no test coverage detected