| 2091 | } |
| 2092 | |
| 2093 | void Planner::buildPlanForQueryNode() |
| 2094 | { |
| 2095 | ProfileEvents::increment(ProfileEvents::SelectQueriesWithSubqueries); |
| 2096 | ProfileEvents::increment(ProfileEvents::QueriesWithSubqueries); |
| 2097 | |
| 2098 | auto & query_node = query_tree->as<QueryNode &>(); |
| 2099 | const auto & query_context = planner_context->getQueryContext(); |
| 2100 | if (query_node.hasWhere()) |
| 2101 | { |
| 2102 | auto condition_constant = tryExtractConstantFromConditionNode(query_node.getWhere()); |
| 2103 | if (condition_constant.has_value() && *condition_constant) |
| 2104 | query_node.getWhere() = {}; |
| 2105 | } |
| 2106 | |
| 2107 | SelectQueryInfo select_query_info = buildSelectQueryInfo(); |
| 2108 | const Settings & settings = query_context->getSettingsRef(); |
| 2109 | |
| 2110 | QueryResultCachePtr query_result_cache = planner_context->getMutableQueryContext()->getQueryResultCache(); |
| 2111 | bool can_use_query_result_cache = query_context->getCanUseQueryResultCache(); |
| 2112 | ASTPtr ast = select_query_info.query; |
| 2113 | |
| 2114 | /// Determine if the query result cache should be used for this query node (with `is_subquery = true` key). |
| 2115 | /// The Planner-level cache is separate from the `executeQuery`-level cache (`is_subquery = false`). |
| 2116 | /// Rules: |
| 2117 | /// 1. For actual subqueries: explicit SETTINGS `use_query_cache` on the node wins |
| 2118 | /// 2. `query_cache_for_subqueries` propagates from outer query (applies to all Planner invocations) |
| 2119 | /// 3. By default, `use_query_cache` does NOT propagate from outer query to subqueries |
| 2120 | bool should_cache = shouldUseQueryCacheForSubquery(query_node, can_use_query_result_cache, settings, |
| 2121 | select_query_options.is_subquery, query_context); |
| 2122 | |
| 2123 | /// Query result cache must actually exist |
| 2124 | if (should_cache && !query_result_cache) |
| 2125 | should_cache = false; |
| 2126 | |
| 2127 | /// For explicit per-subquery opt-in, we track cache eligibility locally |
| 2128 | /// without mutating the shared query context (which would leak to the outer query). |
| 2129 | bool local_can_use_cache = can_use_query_result_cache || should_cache; |
| 2130 | |
| 2131 | /// If the query runs with "use_query_cache = 1", we first probe if the query cache already contains the query result (if yes: |
| 2132 | /// return result from cache). If doesn't, we execute the query normally and write the result into the query cache. Both steps use a |
| 2133 | /// hash of the AST, the current database and the settings as cache key. Unfortunately, the settings are in some places internally |
| 2134 | /// modified between steps 1 and 2 (= during query execution) - this is silly but hard to forbid. As a result, the hashes no longer |
| 2135 | /// match and the cache is rendered ineffective. Therefore make a copy of the settings and use it for steps 1 and 2. |
| 2136 | std::optional<Settings> settings_copy; |
| 2137 | if (local_can_use_cache) |
| 2138 | settings_copy = settings; |
| 2139 | |
| 2140 | /// If it is a non-internal SELECT, and passive (read) use of the query cache is enabled, and the cache knows the query, then add a ReadFromQueryResultCacheStep instead of building the rest of the plan. |
| 2141 | if (should_cache && settings[Setting::enable_reads_from_query_cache]) |
| 2142 | { |
| 2143 | QueryResultCache::Key key(ast, query_context->getCurrentDatabase(), *settings_copy, query_context->getCurrentQueryId(), query_context->getUserID(), query_context->getCurrentRoles(), /* is_subquery = */ true); |
| 2144 | auto reader = std::make_shared<QueryResultCacheReader>(query_result_cache->createReader(key)); |
| 2145 | if (reader->hasCacheEntryForKey()) |
| 2146 | { |
| 2147 | addReadFromQueryResultCacheStep(query_plan, reader->getSource(), reader->getSourceTotals(), reader->getSourceExtremes()); |
| 2148 | return; |
| 2149 | } |
| 2150 | } |
nothing calls this directly
no test coverage detected