Finer-grained subquery cache propagation control#99804
Conversation
|
Thanks! This is exactly what we need! |
d6f0b0a to
107a40c
Compare
|
@alexey-milovidov Here's the status on implementing your feedback for finer-grained subquery The three rules from your feedback are implemented in
Along the way we also fixed two issues from the base PR (#76252):
Does the overall approach look right to you? |
|
@rschu1ze @alexey-milovidov I think we're done here, let me know! |
|
@rschu1ze @alexey-milovidov do we still want this? |
|
Yes. Haven't had time to check yet, sorry. |
|
The Stress test (arm_msan) failure is fixed by #101239, which should be merged first. After it is merged, please update the branch to include the fix. |
|
The failures of "Flaky check" in "functions_bad_arguments" will be fixed by #101994. |
Address review feedback on PR ClickHouse#99804. The error messages thrown by `checkCanWriteQueryResultCache` for queries containing non-deterministic functions or system tables had the awkward "regardless or to omit caching" phrasing. Reword to "regardless, or omit caching" for clarity. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address review feedback on PR ClickHouse#99804. `removeTableAliases` used `starts_with("__table")` to strip planner-generated table aliases from the AST before computing the subquery cache key. Any user-visible identifier starting with the same prefix (e.g. `__table_prod`) was incorrectly normalized away, so a query like `SELECT x FROM __table_prod` could collide with `SELECT x FROM prod` in the subquery cache and return wrong results. The analyzer's `createUniqueAliasesIfNecessary` generates aliases of the exact form `__table<N>` where `N` is a non-empty sequence of digits (`__table1`, `__table2`, etc.). Restrict the alias-stripping check to that exact pattern via a new `isPlannerGeneratedTableAlias` helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…vior After commit abf0a27 gated `query_cache_for_subqueries` Planner-level caching on `is_subquery`, the top-level query is no longer recorded under the `is_subquery = 1` namespace — only actual subqueries are. The 03381_* test references and SQL comments were still asserting the old "outer + inner" count and so the Fast test was failing on all 10 affected tests. - Update test references to match the new actual outputs. - Update misleading SQL comments that referred to the outer node as a subquery. - Move `query_cache_for_subqueries` from the `26.4` to the `26.5` section of `SettingsChangesHistory.cpp` so the upgrade-from-26.4 check in `02995_new_settings_history` recognises it. CI report: ClickHouse#99804 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| } | ||
|
|
||
| IASTHash calculateASTHash(ASTPtr ast, const String & current_database, const Settings & settings) | ||
| IASTHash calculateAstHash(ASTPtr ast, const String & current_database, const Settings & settings, const bool is_subquery, const bool already_cloned = false) |
There was a problem hiding this comment.
Our code style demands capital letters for every abbreviated word. AST, not Ast.
There was a problem hiding this comment.
Renamed in 47aecf5 — calculateAstHash → calculateASTHash and calculateAstHashAndQueryString → calculateASTHashAndQueryString.
alexey-milovidov
left a comment
There was a problem hiding this comment.
A few rough edges, but mostly good.
Address review feedback on PR ClickHouse#99804. The error messages thrown by `checkCanWriteQueryResultCache` for queries containing non-deterministic functions or system tables had the awkward "regardless or to omit caching" phrasing. Reword to "regardless, or omit caching" for clarity. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address review feedback on PR ClickHouse#99804. `removeTableAliases` used `starts_with("__table")` to strip planner-generated table aliases from the AST before computing the subquery cache key. Any user-visible identifier starting with the same prefix (e.g. `__table_prod`) was incorrectly normalized away, so a query like `SELECT x FROM __table_prod` could collide with `SELECT x FROM prod` in the subquery cache and return wrong results. The analyzer's `createUniqueAliasesIfNecessary` generates aliases of the exact form `__table<N>` where `N` is a non-empty sequence of digits (`__table1`, `__table2`, etc.). Restrict the alias-stripping check to that exact pattern via a new `isPlannerGeneratedTableAlias` helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address review feedback on PR ClickHouse#99804. `shouldUseQueryCacheForSubquery` was returning `true` for the top-level query when both `use_query_cache = 1` and `query_cache_for_subqueries = 1` were set. That caused the top-level query to be cached twice: once by `executeQuery` under the `is_subquery = false` key, and once again by the Planner under the `is_subquery = true` key. Gate the `query_cache_for_subqueries` propagation branch on `is_subquery` so only real subqueries enter the Planner-level cache namespace, matching the setting's contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…vior After commit abf0a27 gated `query_cache_for_subqueries` Planner-level caching on `is_subquery`, the top-level query is no longer recorded under the `is_subquery = 1` namespace — only actual subqueries are. The 03381_* test references and SQL comments were still asserting the old "outer + inner" count and so the Fast test was failing on all 10 affected tests. - Update test references to match the new actual outputs. - Update misleading SQL comments that referred to the outer node as a subquery. - Move `query_cache_for_subqueries` from the `26.4` to the `26.5` section of `SettingsChangesHistory.cpp` so the upgrade-from-26.4 check in `02995_new_settings_history` recognises it. CI report: ClickHouse#99804 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4de2595 to
ca58e56
Compare
Implements finer-grained subquery cache propagation control, as requested in the review of ClickHouse#76252. `use_query_cache` on the outer query no longer auto-propagates to subqueries. Three rules implemented in `shouldUseQueryCacheForSubquery`: 1. No auto-propagation by default: `use_query_cache` on the outer query does not propagate to subqueries. 2. Explicit per-subquery opt-in: `SELECT * FROM (SELECT ... SETTINGS use_query_cache = true)` enables caching for that specific subquery. 3. Bulk propagation: `query_cache_for_subqueries = true` enables propagation into all subqueries. Also fixes two issues from ClickHouse#76252: - Config key mismatch: the rename `QueryCache` -> `QueryResultCache` changed keys in `Context.cpp` but not in config files, breaking `SYSTEM RELOAD CONFIG` for cache settings. - All `03381_*` tests now explicitly `SET enable_analyzer = 1` since Planner-level subquery caching is new-analyzer only. Co-authored-by: Баранник Никита Павлович <npbarannik@edu.hse.ru> Co-authored-by: Robert Schulze <robert@clickhouse.com> Co-authored-by: Alexey Milovidov <milovidov@clickhouse.com>
ca58e56 to
c494ad1
Compare
| /// When should_cache is true but the outer query didn't set use_query_cache (explicit subquery opt-in), | ||
| /// skip the context flag check in checkCanWriteQueryResultCache while still respecting safety checks. | ||
| bool skip_context_check = should_cache && !can_use_query_result_cache; | ||
| if (should_cache && checkCanWriteQueryResultCache(ast, query_context, skip_context_check)) |
There was a problem hiding this comment.
This write path can cache subquery results even when the outer query has use_query_cache = 0 (explicit per-subquery opt-in uses skip_context_check = true). In that case we bypass the non-throw overflow-mode guard that only exists in executeQuery.
That reintroduces the truncated-result risk from bug 67476 for subqueries: a subquery can run with *_overflow_mode != 'throw', produce partial output, and still be written into the cache.
Could we apply the same overflow-mode precondition here (or inside checkCanWriteQueryResultCache) before adding StreamInQueryResultCacheStep?
LLVM Coverage Report
Changed lines: 94.38% (302/320) | lost baseline coverage: 9 line(s) · Uncovered code |
|
Hi @vvo @alexey-milovidov — while reviewing this PR I found the following:
Happy to discuss — close anything that's wrong or already addressed. |

Implements finer-grained subquery cache propagation control, as requested in the review of #76252 by @alexey-milovidov.
use_query_cacheon the outer query no longer auto-propagates to subqueries. Three rules implemented inshouldUseQueryCacheForSubquery:use_query_cacheon the outer query does NOT propagate to subqueriesSELECT * FROM (SELECT ... SETTINGS use_query_cache = true)enables caching for that specific subqueryquery_cache_for_subqueries = trueenables propagation into all subqueriesAlso fixes two issues from #76252:
QueryCache→QueryResultCachechanged keys inContext.cppbut not in config files, breakingSYSTEM RELOAD CONFIGfor cache settings03381_*tests now explicitlySET enable_analyzer = 1since Planner-level subquery caching is new-analyzer onlyChangelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
Individual subquery results can now be cached independently using
SETTINGS use_query_cache = trueon specific subqueries, without caching the entire outer query. A new settingquery_cache_for_subqueries = trueenables bulk propagation ofuse_query_cacheinto all subqueries. Note:use_query_cacheon the outer query no longer auto-propagates to subqueries.Documentation entry for user-facing changes
A new "Subquery Caching" section has been added to the query cache documentation explaining the three propagation modes with examples.
Version info
26.5.1.844