Repository: ClickHouse/ClickHouse
File: src/Storages/buildQueryTreeForShard.cpp (C++)

Code before:
            /// (like the TABLE node inside the ARRAY_JOIN) won't be added to the old-to-new pointer map.
            /// ColumnNodes in the outer query that reference these inner sources via weak_ptr would
            /// become dangling after the old tree is dropped. To prevent this, add all inner column
            /// sources to the replacement map so their weak pointers get properly remapped.
            if (auto * array_join_node = join_table_expression->as<ArrayJoinNode>())
            {
                std::stack<QueryTreeNodePtr> inner_sources;
                inner_sources.push(array_join_node->getTableExpression());
                while (!inner_sources.empty())
                {
                    auto current = inner_sources.top();
                    inner_sources.pop();

                    replacement_map.emplace(current.get(), temporary_table_expression_node);

                    if (auto * inner_join = current->as<JoinNode>())
                    {
                        inner_sources.push(inner_join->getLeftTableExpression());
                        inner_sources.push(inner_join->getRightTableExpression());
                    }
                    else if (auto * inner_array_join = current->as<ArrayJoinNode>())
                    {
                        inner_sources.push(inner_array_join->getTableExpression());
                    }
                }
            }

            replacement_map.emplace(join_table_expression.get(), std::move(temporary_table_expression_node));
            continue;
        }
        if (auto * in_function_node = global_in_or_join_node.query_node->as<FunctionNode>())
        {
            auto & in_function_subquery_node = in_function_node->getArguments().getNodes().at(1);
            auto in_function_node_type = in_function_subquery_node->getNodeType();
            if (in_function_node_type != QueryTreeNodeType::QUERY && in_function_node_type != QueryTreeNodeType::UNION
                && in_function_node_type != QueryTreeNodeType::TABLE)
                continue;

            QueryTreeNodePtr replacement_table_expression;
            auto & temporary_table_expression_node = global_in_temporary_tables[in_function_subquery_node];
            if (!temporary_table_expression_node)
            {
                auto subquery_to_execute = in_function_subquery_node;
                if (subquery_to_execute->as<TableNode>())
                    subquery_to_execute = buildSubqueryToReadColumnsFromTableExpression(
                        subquery_to_execute,
                        planner_context->getQueryContext());

                // If DISTINCT optimization is enabled, add DISTINCT before executing the subquery
                if (enable_add_distinct_to_in_subqueries)
                    addDistinctRecursively(subquery_to_execute);

Review comment (suggestion):
It's unclear why it is needed at all. The table expression is being replaced by a temporary table. There's no point to traverse ARRAY JOIN

Code after:
              */
            std::vector<const IQueryTreeNode *> descendants_to_map;
            for (const auto & child : join_table_expression->getChildren())
                if (child)
                    descendants_to_map.push_back(child.get());

            while (!descendants_to_map.empty())
            {
                const auto * descendant = descendants_to_map.back();
                descendants_to_map.pop_back();

                replacement_map.emplace(descendant, temporary_table_expression_node);

                for (const auto & child : descendant->getChildren())
                    if (child)
                        descendants_to_map.push_back(child.get());
            }

            replacement_map.emplace(join_table_expression.get(), std::move(temporary_table_expression_node));
            continue;
        }
        if (auto * in_function_node = global_in_or_join_node.query_node->as<FunctionNode>())
        {
            auto & in_function_subquery_node = in_function_node->getArguments().getNodes().at(1);
            auto in_function_node_type = in_function_subquery_node->getNodeType();
            if (in_function_node_type != QueryTreeNodeType::QUERY && in_function_node_type != QueryTreeNodeType::UNION
                && in_function_node_type != QueryTreeNodeType::TABLE)
                continue;

            QueryTreeNodePtr replacement_table_expression;
            auto & temporary_table_expression_node = global_in_temporary_tables[in_function_subquery_node];
            if (!temporary_table_expression_node)
            {
                auto subquery_to_execute = in_function_subquery_node;
                if (subquery_to_execute->as<TableNode>())
                    subquery_to_execute = buildSubqueryToReadColumnsFromTableExpression(
                        subquery_to_execute,
                        planner_context->getQueryContext());

                // If DISTINCT optimization is enabled, add DISTINCT before executing the subquery
                if (enable_add_distinct_to_in_subqueries)
                    addDistinctRecursively(subquery_to_execute);

                temporary_table_expression_node = executeSubqueryNode(
                    subquery_to_execute,
                    planner_context->getMutableQueryContext(),
                    global_in_or_join_node.subquery_depth);
                replacement_table_expression = temporary_table_expression_node;
            }
            else
            {