Resolve query tree matcher. Check MatcherNode.h for detailed matcher description. Check ColumnTransformers.h for detailed transformers description. * * 1. Populate matched expression nodes resolving qualified or unqualified matcher. * 2. Apply column transformers to matched expression nodes. For strict column transformers save used column names. * 3. Validate strict column transformers.
| 2316 | * 3. Validate strict column transformers. |
| 2317 | */ |
| 2318 | ProjectionNames QueryAnalyzer::resolveMatcher(QueryTreeNodePtr & matcher_node, IdentifierResolveScope & scope) |
| 2319 | { |
| 2320 | auto & matcher_node_typed = matcher_node->as<MatcherNode &>(); |
| 2321 | |
| 2322 | std::unordered_map<std::string, QueryTreeNodePtr> replace_transformer_mappings; |
| 2323 | |
| 2324 | QueryTreeNodesWithNames matched_expression_nodes_with_names; |
| 2325 | |
| 2326 | if (matcher_node_typed.isQualified()) |
| 2327 | matched_expression_nodes_with_names = resolveQualifiedMatcher(matcher_node, scope); |
| 2328 | else |
| 2329 | matched_expression_nodes_with_names = resolveUnqualifiedMatcher(matcher_node, scope); |
| 2330 | |
| 2331 | if (scope.join_use_nulls) |
| 2332 | { |
| 2333 | /** If we are resolving matcher came from the result of JOIN and `join_use_nulls` is set, |
| 2334 | * we need to convert joined column type to Nullable. |
| 2335 | * We are checking all registered_table_expression_nodes which contains all table expressions that are used to resolve matcher. |
| 2336 | * If it's on null side, we need to convert column type to Nullable. |
| 2337 | */ |
| 2338 | for (const auto & table_expression : scope.registered_table_expression_nodes) |
| 2339 | { |
| 2340 | const JoinNode * nearest_scope_join_node = table_expression->as<JoinNode>(); |
| 2341 | if (!nearest_scope_join_node) |
| 2342 | continue; |
| 2343 | |
| 2344 | for (auto & [node, node_name] : matched_expression_nodes_with_names) |
| 2345 | { |
| 2346 | auto join_identifier_side = getColumnSideFromJoinTree(node, *nearest_scope_join_node); |
| 2347 | if (!join_identifier_side) |
| 2348 | continue; |
| 2349 | auto projection_name_it = node_to_projection_name.find(node); |
| 2350 | auto nullable_node = IdentifierResolver::convertJoinedColumnTypeToNullIfNeeded(node, node->getResultType(), nearest_scope_join_node->getKind(), join_identifier_side, scope); |
| 2351 | if (nullable_node) |
| 2352 | { |
| 2353 | node = nullable_node; |
| 2354 | /// Set the same projection name for new nullable node |
| 2355 | if (projection_name_it != node_to_projection_name.end()) |
| 2356 | { |
| 2357 | node_to_projection_name.emplace(node, projection_name_it->second); |
| 2358 | } |
| 2359 | } |
| 2360 | } |
| 2361 | } |
| 2362 | } |
| 2363 | |
| 2364 | /// When an APPLY transformer creates an aggregate function (e.g. `* APPLY x -> argMax(x, number)` |
| 2365 | /// or `* APPLY x -> toString(argMax(x, number))`), the matched columns must NOT be converted to |
| 2366 | /// Nullable here. Aggregate function arguments use pre-aggregation types (non-Nullable); the Nullable |
| 2367 | /// wrapping is handled post-aggregation by Rollup/Cube/GroupingSets transforms. Converting here would |
| 2368 | /// create a type mismatch: the aggregate function would expect Nullable input columns, but the actual |
| 2369 | /// columns in the Aggregating step are non-Nullable. |
| 2370 | /// This causes a crash in AggregateFunctionNullVariadic::addBatchSinglePlace. |
| 2371 | /// |
| 2372 | /// We traverse the APPLY expression tree because the aggregate function may be nested |
| 2373 | /// inside other function calls (e.g. `toString(argMax(x, number))`), not just at the top level. |
| 2374 | /// We use `AggregateFunctionFactory` name lookup (not `FunctionNode::isAggregateFunction`) because |
| 2375 | /// APPLY expressions have not been resolved yet at this point — `FunctionNode::kind` is still `UNKNOWN`. |
nothing calls this directly
no test coverage detected