|
18 | 18 | #include <Functions/IFunctionAdaptors.h> |
19 | 19 | #include <Functions/UserDefined/UserDefinedExecutableFunctionFactory.h> |
20 | 20 | #include <Functions/UserDefined/UserDefinedSQLFunctionFactory.h> |
| 21 | +#include <Functions/exists.h> |
21 | 22 | #include <Functions/grouping.h> |
22 | 23 |
|
23 | 24 | #include <TableFunctions/TableFunctionFactory.h> |
@@ -113,6 +114,7 @@ namespace Setting |
113 | 114 | extern const SettingsBool allow_suspicious_types_in_order_by; |
114 | 115 | extern const SettingsBool allow_not_comparable_types_in_order_by; |
115 | 116 | extern const SettingsBool use_concurrency_control; |
| 117 | + extern const SettingsBool allow_experimental_correlated_subqueries; |
116 | 118 | extern const SettingsString implicit_table_at_top_level; |
117 | 119 | } |
118 | 120 |
|
@@ -1380,12 +1382,14 @@ IdentifierResolveResult QueryAnalyzer::tryResolveIdentifierInParentScopes(const |
1380 | 1382 | { |
1381 | 1383 | auto current = nodes_to_process.back(); |
1382 | 1384 | nodes_to_process.pop_back(); |
1383 | | - if (auto * current_column = current->as<ColumnNode>()) |
| 1385 | + if (ColumnNodePtr current_column = std::dynamic_pointer_cast<ColumnNode>(current)) |
1384 | 1386 | { |
1385 | | - if (isDependentColumn(&scope, current_column->getColumnSource())) |
| 1387 | + auto is_correlated_column = checkCorrelatedColumn(&scope, current_column); |
| 1388 | + if (is_correlated_column && !scope.context->getSettingsRef()[Setting::allow_experimental_correlated_subqueries]) |
1386 | 1389 | { |
1387 | 1390 | throw Exception(ErrorCodes::UNSUPPORTED_METHOD, |
1388 | | - "Resolved identifier '{}' in parent scope to expression '{}' with correlated column '{}'. In scope {}", |
| 1391 | + "Resolved identifier '{}' in parent scope to expression '{}' with correlated column '{}'" |
| 1392 | + " (Enable 'allow_experimental_correlated_subqueries' setting to allow correlated subqueries execution). In scope {}", |
1389 | 1393 | identifier_lookup.identifier.getFullName(), |
1390 | 1394 | resolved_identifier->formatASTForErrorMessage(), |
1391 | 1395 | current_column->getColumnName(), |
@@ -2848,27 +2852,6 @@ ProjectionNames QueryAnalyzer::resolveFunction(QueryTreeNodePtr & node, Identifi |
2848 | 2852 | } |
2849 | 2853 | } |
2850 | 2854 |
|
2851 | | - if (is_special_function_exists) |
2852 | | - { |
2853 | | - checkFunctionNodeHasEmptyNullsAction(*function_node_ptr); |
2854 | | - /// Rewrite EXISTS (subquery) into 1 IN (SELECT 1 FROM (subquery) LIMIT 1). |
2855 | | - auto & exists_subquery_argument = function_node_ptr->getArguments().getNodes().at(0); |
2856 | | - |
2857 | | - auto constant_data_type = std::make_shared<DataTypeUInt64>(); |
2858 | | - |
2859 | | - auto in_subquery = std::make_shared<QueryNode>(Context::createCopy(scope.context)); |
2860 | | - in_subquery->setIsSubquery(true); |
2861 | | - in_subquery->getProjection().getNodes().push_back(std::make_shared<ConstantNode>(1UL, constant_data_type)); |
2862 | | - in_subquery->getJoinTree() = exists_subquery_argument; |
2863 | | - in_subquery->getLimit() = std::make_shared<ConstantNode>(1UL, constant_data_type); |
2864 | | - |
2865 | | - function_node_ptr = std::make_shared<FunctionNode>("in"); |
2866 | | - function_node_ptr->getArguments().getNodes() = {std::make_shared<ConstantNode>(1UL, constant_data_type), in_subquery}; |
2867 | | - node = function_node_ptr; |
2868 | | - function_name = "in"; |
2869 | | - is_special_function_in = true; |
2870 | | - } |
2871 | | - |
2872 | 2855 | if (is_special_function_if && !function_node_ptr->getArguments().getNodes().empty()) |
2873 | 2856 | { |
2874 | 2857 | checkFunctionNodeHasEmptyNullsAction(*function_node_ptr); |
@@ -2925,12 +2908,61 @@ ProjectionNames QueryAnalyzer::resolveFunction(QueryTreeNodePtr & node, Identifi |
2925 | 2908 | } |
2926 | 2909 |
|
2927 | 2910 | /// Resolve function arguments |
2928 | | - bool allow_table_expressions = is_special_function_in; |
| 2911 | + bool allow_table_expressions = is_special_function_in || is_special_function_exists; |
2929 | 2912 | auto arguments_projection_names = resolveExpressionNodeList(function_node_ptr->getArgumentsNode(), |
2930 | 2913 | scope, |
2931 | 2914 | true /*allow_lambda_expression*/, |
2932 | 2915 | allow_table_expressions /*allow_table_expression*/); |
2933 | 2916 |
|
| 2917 | + if (is_special_function_exists) |
| 2918 | + { |
| 2919 | + checkFunctionNodeHasEmptyNullsAction(*function_node_ptr); |
| 2920 | + /// Rewrite EXISTS (subquery) into 1 IN (SELECT 1 FROM (subquery) LIMIT 1). |
| 2921 | + auto & exists_subquery_argument = function_node_ptr->getArguments().getNodes().at(0); |
| 2922 | + bool correlated_exists_subquery = exists_subquery_argument->getNodeType() == QueryTreeNodeType::QUERY |
| 2923 | + ? exists_subquery_argument->as<QueryNode>()->isCorrelated() |
| 2924 | + : exists_subquery_argument->as<UnionNode>()->isCorrelated(); |
| 2925 | + if (!correlated_exists_subquery) |
| 2926 | + { |
| 2927 | + auto constant_data_type = std::make_shared<DataTypeUInt64>(); |
| 2928 | + |
| 2929 | + auto in_subquery = std::make_shared<QueryNode>(Context::createCopy(scope.context)); |
| 2930 | + in_subquery->setIsSubquery(true); |
| 2931 | + in_subquery->getProjection().getNodes().push_back(std::make_shared<ConstantNode>(1UL, constant_data_type)); |
| 2932 | + in_subquery->getJoinTree() = exists_subquery_argument; |
| 2933 | + in_subquery->getLimit() = std::make_shared<ConstantNode>(1UL, constant_data_type); |
| 2934 | + |
| 2935 | + function_node_ptr = std::make_shared<FunctionNode>("in"); |
| 2936 | + function_node_ptr->getArguments().getNodes() = { |
| 2937 | + std::make_shared<ConstantNode>(1UL, constant_data_type), |
| 2938 | + std::move(in_subquery) |
| 2939 | + }; |
| 2940 | + |
| 2941 | + /// Resolve modified arguments |
| 2942 | + arguments_projection_names = resolveExpressionNodeList(function_node_ptr->getArgumentsNode(), |
| 2943 | + scope, |
| 2944 | + true /*allow_lambda_expression*/, |
| 2945 | + true /*allow_table_expression*/); |
| 2946 | + |
| 2947 | + node = function_node_ptr; |
| 2948 | + function_name = "in"; |
| 2949 | + is_special_function_in = true; |
| 2950 | + } |
| 2951 | + else |
| 2952 | + { |
| 2953 | + /// Subquery is correlated and EXISTS can not be replaced by IN function. |
| 2954 | + /// EXISTS function will be replated by JOIN during query planning. |
| 2955 | + auto function_exists = std::make_shared<FunctionExists>(); |
| 2956 | + function_node_ptr->resolveAsFunction( |
| 2957 | + std::make_shared<FunctionToFunctionBaseAdaptor>( |
| 2958 | + function_exists, DataTypes{}, function_exists->getReturnTypeImpl({}) |
| 2959 | + ) |
| 2960 | + ); |
| 2961 | + |
| 2962 | + return { calculateFunctionProjectionName(node, parameters_projection_names, arguments_projection_names) }; |
| 2963 | + } |
| 2964 | + } |
| 2965 | + |
2934 | 2966 | /// Mask arguments if needed |
2935 | 2967 | if (!scope.context->getSettingsRef()[Setting::format_display_secrets_in_show_and_select]) |
2936 | 2968 | { |
@@ -2981,6 +3013,10 @@ ProjectionNames QueryAnalyzer::resolveFunction(QueryTreeNodePtr & node, Identifi |
2981 | 3013 | throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, "Function '{}' expects 2 arguments", function_name); |
2982 | 3014 |
|
2983 | 3015 | auto & in_second_argument = function_in_arguments_nodes[1]; |
| 3016 | + if (isCorrelatedQueryOrUnionNode(function_in_arguments_nodes[0]) || isCorrelatedQueryOrUnionNode(function_in_arguments_nodes[1])) |
| 3017 | + throw Exception(ErrorCodes::NOT_IMPLEMENTED, |
| 3018 | + "Correlated subqueries are not supported as IN function arguments yet, but found in expression: {}", |
| 3019 | + node->formatASTForErrorMessage()); |
2984 | 3020 | auto * table_node = in_second_argument->as<TableNode>(); |
2985 | 3021 | auto * table_function_node = in_second_argument->as<TableFunctionNode>(); |
2986 | 3022 |
|
@@ -5143,6 +5179,16 @@ void QueryAnalyzer::resolveJoin(QueryTreeNodePtr & join_node, IdentifierResolveS |
5143 | 5179 | resolveQueryJoinTreeNode(join_node_typed.getRightTableExpression(), scope, expressions_visitor); |
5144 | 5180 | validateJoinTableExpressionWithoutAlias(join_node, join_node_typed.getRightTableExpression(), scope); |
5145 | 5181 |
|
| 5182 | + if (isCorrelatedQueryOrUnionNode(join_node_typed.getLeftTableExpression())) |
| 5183 | + throw Exception(ErrorCodes::NOT_IMPLEMENTED, |
| 5184 | + "Correlated subqueries are not supported in JOINs yet, but found in expression: {}", |
| 5185 | + join_node_typed.getLeftTableExpression()->formatASTForErrorMessage()); |
| 5186 | + |
| 5187 | + if (isCorrelatedQueryOrUnionNode(join_node_typed.getRightTableExpression())) |
| 5188 | + throw Exception(ErrorCodes::NOT_IMPLEMENTED, |
| 5189 | + "Correlated subqueries are not supported in JOINs yet, but found in expression: {}", |
| 5190 | + join_node_typed.getRightTableExpression()->formatASTForErrorMessage()); |
| 5191 | + |
5146 | 5192 | if (!join_node_typed.getLeftTableExpression()->hasAlias() && !join_node_typed.getRightTableExpression()->hasAlias()) |
5147 | 5193 | checkDuplicateTableNamesOrAliasForPasteJoin(join_node_typed, scope); |
5148 | 5194 |
|
|
0 commit comments