Repository: ClickHouse/ClickHouse
File: src/Analyzer/Resolve/QueryAnalyzer.cpp (C++)

Code before:

        auto skip_argument_index_it = std::find(skip_analysis_arguments_indexes.begin(),
            skip_analysis_arguments_indexes.end(),
            table_function_argument_index);
        if (skip_argument_index_it != skip_analysis_arguments_indexes.end())
        {
            result_table_function_arguments.push_back(table_function_argument);
            continue;
        }

        if (auto * identifier_node = table_function_argument->as<IdentifierNode>())
        {
            const auto & unresolved_identifier = identifier_node->getIdentifier();
            auto identifier_resolve_result = tryResolveIdentifier({unresolved_identifier, IdentifierLookupContext::EXPRESSION}, scope);
            auto resolved_identifier = std::move(identifier_resolve_result.resolved_identifier);

            if (resolved_identifier && resolved_identifier->getNodeType() == QueryTreeNodeType::CONSTANT)
                result_table_function_arguments.push_back(std::move(resolved_identifier));
            else
                result_table_function_arguments.push_back(table_function_argument);

            continue;
        }
        if (auto * table_function_argument_function = table_function_argument->as<FunctionNode>())
        {
            const auto & table_function_argument_function_name = table_function_argument_function->getFunctionName();
            if (TableFunctionFactory::instance().isTableFunctionName(table_function_argument_function_name))
            {
                auto table_function_node_to_resolve_typed = std::make_shared<TableFunctionNode>(table_function_argument_function_name);
                table_function_node_to_resolve_typed->getArgumentsNode() = table_function_argument_function->getArgumentsNode();

                QueryTreeNodePtr table_function_node_to_resolve = std::move(table_function_node_to_resolve_typed);
                if (table_function_argument_function_name == "view")
                {
                    /// Subquery in view() table function can reference tables that don't exist on the initiator.
                    /// In the following example `users` table may be not available on the initiator:
                    /// SELECT *
                    /// FROM remoteSecure(<address>, view(
                    ///     SELECT
                    ///         t1.age,
                    ///         t1.name,
                    ///         t2.name
                    ///     FROM users AS t1
                    ///     INNER JOIN users AS t2 ON t1.uid = t2.uid
                    /// ), <user>, <password>)
                    /// SETTINGS prefer_localhost_replica = 0
                    skip_analysis_arguments_indexes.push_back(table_function_argument_index);
                }
                else
                {
                    resolveTableFunction(table_function_node_to_resolve, scope, expressions_visitor, true /*nested_table_function*/);

Review comment (suggestion):
Perhaps we can do deep copy there, call `clone` for `left_table_expression` as we do for `projection_node`

https://github.com/ClickHouse/ClickHouse/blob/c2f982ed1b920c8a68947c35cc719a1d215c49da/src/Analyzer/Resolve/QueryAnalyzer.cpp#L4428-L4433

Since this feature was implemented in a rather hacky way, I tend not to expose any of its side effects to the rest of the codebase.

Code after:

        auto skip_argument_index_it = std::find(skip_analysis_arguments_indexes.begin(),
            skip_analysis_arguments_indexes.end(),
            table_function_argument_index);
        if (skip_argument_index_it != skip_analysis_arguments_indexes.end())
        {
            result_table_function_arguments.push_back(table_function_argument);
            continue;
        }

        if (auto * identifier_node = table_function_argument->as<IdentifierNode>())
        {
            const auto & unresolved_identifier = identifier_node->getIdentifier();
            auto identifier_resolve_result = tryResolveIdentifier({unresolved_identifier, IdentifierLookupContext::EXPRESSION}, scope);
            auto resolved_identifier = std::move(identifier_resolve_result.resolved_identifier);

            if (resolved_identifier && resolved_identifier->getNodeType() == QueryTreeNodeType::CONSTANT)
                result_table_function_arguments.push_back(std::move(resolved_identifier));
            else
                result_table_function_arguments.push_back(table_function_argument);

            continue;
        }
        /// Argument is already a resolved TableFunctionNode (e.g. when the parent table function node
        /// is re-analyzed in a synthetic subquery created by `analyzer_compatibility_join_using_top_level_identifier`).
        if (table_function_argument->as<TableFunctionNode>())
        {
            result_table_function_arguments.push_back(table_function_argument);
            continue;
        }

        if (auto * table_function_argument_function = table_function_argument->as<FunctionNode>())
        {
            const auto & table_function_argument_function_name = table_function_argument_function->getFunctionName();
            if (TableFunctionFactory::instance().isTableFunctionName(table_function_argument_function_name))
            {
                auto table_function_node_to_resolve_typed = std::make_shared<TableFunctionNode>(table_function_argument_function_name);
                table_function_node_to_resolve_typed->getArgumentsNode() = table_function_argument_function->getArgumentsNode();

                QueryTreeNodePtr table_function_node_to_resolve = std::move(table_function_node_to_resolve_typed);
                if (table_function_argument_function_name == "view")
                {
                    /// Subquery in view() table function can reference tables that don't exist on the initiator.
                    /// In the following example `users` table may be not available on the initiator:
                    /// SELECT *
                    /// FROM remoteSecure(<address>, view(
                    ///     SELECT
                    ///         t1.age,
                    ///         t1.name,
                    ///         t2.name
                    ///     FROM users AS t1