mirror of
https://github.com/hanzoai/datastore.git
synced 2026-09-26 13:55:07 +00:00
With the analyzer, `WITH`-clause expression aliases are shared by pointer in the query tree (a DAG), but `PlannerActionsVisitorImpl::visitFunction` re-traversed shared subtrees once per reference. For deeply nested aliases (each referencing the previous one several times) this is exponential in the chain depth, so building the `ActionsDAG` could dominate query planning time. When there is a single actions scope (no lambda is being built), `visitFunction` now reuses an action node whose name already exists in the scope instead of re-traversing it. The `ActionsDAG` already de-duplicates added nodes via `addFunctionIfNecessary`, so the resulting DAG is unchanged - this only avoids redundant traversal and the associated hashing of large action node names. Keying on the action node name additionally reuses identical repeated subexpressions that are not aliased. This does not touch the analyzer pass manager, which re-traverses the same shared DAG and remains the larger cost for such queries. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
34 lines
2.1 KiB
XML
34 lines
2.1 KiB
XML
<test>
|
|
<!--
|
|
A chain of WITH-clause aliases where each alias is a deeply nested `if` expression that
|
|
references the previous alias several times (col2 uses col1 five times, col3 uses col2 five
|
|
times, ...). With the analyzer the aliases are shared by pointer in the query tree (a DAG),
|
|
so building the ActionsDAG used to re-traverse the shared subtrees once per reference, which
|
|
is exponential in the chain depth. `PlannerActionsVisitor::visitFunction` now reuses an
|
|
action node whose name already exists in the single actions scope instead of re-traversing
|
|
it, removing the exponential planner cost.
|
|
|
|
The input column is non-constant (so the chain is not constant-folded and the planner path
|
|
is actually exercised). Depth is kept moderate so the query stays sub-second.
|
|
-->
|
|
<settings>
|
|
<enable_analyzer>1</enable_analyzer>
|
|
<max_expanded_ast_elements>5000000000</max_expanded_ast_elements>
|
|
</settings>
|
|
|
|
<query><![CDATA[
|
|
WITH
|
|
if(value = 10, 'A', if(value = 20, 'B', if(value = 30, 'C', if(value = 40, 'D', if(value = 50, 'E', 'F'))))) AS col1,
|
|
if(col1 = 'A', 'Alpha', if(col1 = 'B', 'Beta', if(col1 = 'C', 'Gamma', if(col1 = 'D', 'Delta', if(col1 = 'E', 'Epsilon', 'Other'))))) AS col2,
|
|
if(col2 = 'Alpha', 1, if(col2 = 'Beta', 2, if(col2 = 'Gamma', 3, if(col2 = 'Delta', 4, if(col2 = 'Epsilon', 5, 0))))) AS col3,
|
|
if(col3 = 1, 'One', if(col3 = 2, 'Two', if(col3 = 3, 'Three', if(col3 = 4, 'Four', if(col3 = 5, 'Five', 'Zero'))))) AS col4,
|
|
if(col4 = 'One', 'Uno', if(col4 = 'Two', 'Dos', if(col4 = 'Three', 'Tres', if(col4 = 'Four', 'Cuatro', if(col4 = 'Five', 'Cinco', 'Cero'))))) AS col5,
|
|
if(col5 = 'Uno', 'I', if(col5 = 'Dos', 'II', if(col5 = 'Tres', 'III', if(col5 = 'Cuatro', 'IV', if(col5 = 'Cinco', 'V', 'Other'))))) AS col6,
|
|
if(col6 = 'I', 'Primero', if(col6 = 'II', 'Segundo', if(col6 = 'III', 'Tercero', if(col6 = 'IV', 'Cuarto', if(col6 = 'V', 'Quinto', 'Otro'))))) AS col7
|
|
SELECT col1, col2, col3, col4, col5, col6, col7
|
|
FROM (SELECT number AS value FROM system.numbers LIMIT 100)
|
|
FORMAT Null
|
|
]]></query>
|
|
|
|
</test>
|