#pragma once #include #include #include #include #include #include #include #include #include namespace DB { /// Recursive traversal and check for optimizeAggregateFunctionsOfGroupByKeys struct KeepAggregateFunctionMatcher { struct Data { const NameSet & group_by_keys; bool keep_aggregator; }; using Visitor = InDepthNodeVisitor; static bool needChildVisit(const ASTPtr & node, const ASTPtr &) { return !(node->as()); } static void visit(ASTFunction & function_node, Data & data) { if (function_node.arguments->children.empty()) { data.keep_aggregator = true; return; } if (!data.group_by_keys.count(function_node.getColumnName())) { Visitor(data).visit(function_node.arguments); } } static void visit(ASTIdentifier & ident, Data & data) { /// if variable of a function is not in GROUP BY keys, this function should not be deleted if (!data.group_by_keys.count(ident.getColumnName())) data.keep_aggregator = true; } static void visit(const ASTPtr & ast, Data & data) { if (data.keep_aggregator) return; if (auto * function_node = ast->as()) { visit(*function_node, data); } else if (auto * ident = ast->as()) { visit(*ident, data); } else if (!ast->as()) { data.keep_aggregator = true; } } }; using KeepAggregateFunctionVisitor = KeepAggregateFunctionMatcher::Visitor; class SelectAggregateFunctionOfGroupByKeysMatcher { public: struct Data { const NameSet & group_by_keys; }; static bool needChildVisit(const ASTPtr & node, const ASTPtr &) { /// Don't descent into table functions and subqueries and special case for ArrayJoin. return !node->as() && !node->as() && !node->as() && !node->as(); } static void visit(ASTPtr & ast, Data & data) { /// Check if function is min/max/any auto * function_node = ast->as(); if (function_node && (function_node->name == "min" || function_node->name == "max" || function_node->name == "any" || function_node->name == "anyLast")) { KeepAggregateFunctionVisitor::Data keep_data{data.group_by_keys, false}; if (function_node->arguments) KeepAggregateFunctionVisitor(keep_data).visit(function_node->arguments); /// Place argument of an aggregate function instead of function if (!keep_data.keep_aggregator && function_node->arguments && !function_node->arguments->children.empty()) { String alias = function_node->alias; ast = (function_node->arguments->children[0])->clone(); ast->setAlias(alias); } } } }; using SelectAggregateFunctionOfGroupByKeysVisitor = InDepthNodeVisitor; }