Skip to main content

radixdb_executor/window/
execute.rs

1use super::*;
2
3impl<'host, H: WindowHost + ?Sized> WindowExecutor<'host, H> {
4    /// Execute SELECT with window functions
5    /// Accepts &[(i64, Row)] to allow RowVec to be passed directly via deref
6    pub(crate) fn execute_select_with_window_functions(
7        &self,
8        stmt: &SelectStatement,
9        ctx: &ExecutionContext,
10        base_rows: &[(i64, Row)],
11        base_columns: &[String],
12    ) -> Result<Box<dyn QueryResult>> {
13        self.execute_select_with_window_functions_internal(
14            stmt,
15            ctx,
16            base_rows,
17            base_columns,
18            None,
19            None,
20        )
21    }
22
23    /// Execute SELECT with window functions, with optional pre-sorted state
24    /// When pre_sorted is Some, rows are already sorted by the specified column,
25    /// allowing us to skip sorting for window functions that ORDER BY the same column
26    pub(crate) fn execute_select_with_window_functions_presorted(
27        &self,
28        stmt: &SelectStatement,
29        ctx: &ExecutionContext,
30        base_rows: &[(i64, Row)],
31        base_columns: &[String],
32        pre_sorted: Option<WindowPreSortedState>,
33    ) -> Result<Box<dyn QueryResult>> {
34        self.execute_select_with_window_functions_internal(
35            stmt,
36            ctx,
37            base_rows,
38            base_columns,
39            pre_sorted,
40            None,
41        )
42    }
43
44    /// Execute SELECT with window functions, with pre-grouped partitions
45    /// When pre_grouped is provided, rows are already grouped by partition column,
46    /// allowing us to skip hash-based grouping for window functions
47    pub(crate) fn execute_select_with_window_functions_pregrouped(
48        &self,
49        stmt: &SelectStatement,
50        ctx: &ExecutionContext,
51        base_rows: &[(i64, Row)],
52        base_columns: &[String],
53        pre_grouped: WindowPreGroupedState,
54    ) -> Result<Box<dyn QueryResult>> {
55        self.execute_select_with_window_functions_internal(
56            stmt,
57            ctx,
58            base_rows,
59            base_columns,
60            None,
61            Some(pre_grouped),
62        )
63    }
64
65    /// Internal implementation with pre-sorted and pre-grouped state parameters
66    pub(super) fn execute_select_with_window_functions_internal(
67        &self,
68        stmt: &SelectStatement,
69        ctx: &ExecutionContext,
70        base_rows: &[(i64, Row)],
71        base_columns: &[String],
72        pre_sorted: Option<WindowPreSortedState>,
73        pre_grouped: Option<WindowPreGroupedState>,
74    ) -> Result<Box<dyn QueryResult>> {
75        // Parse window functions from the SELECT list
76        let window_functions = self.parse_window_functions(stmt, base_columns)?;
77
78        if window_functions.is_empty() {
79            // No window functions found, return base result
80            let mut rows = RowVec::with_capacity(base_rows.len());
81            for (id, row) in base_rows.iter() {
82                rows.push((*id, row.clone()));
83            }
84            return Ok(Box::new(ExecutorResult::new(base_columns.to_vec(), rows)));
85        }
86
87        // OPTIMIZATION: LIMIT pushdown for PARTITION BY queries
88        // Only safe when there is no top-level ORDER BY (otherwise the sort must see all
89        // rows before LIMIT can be applied) and when all window functions share the same
90        // PARTITION BY so one partition map can serve them all.
91        if stmt.order_by.is_empty() && stmt.offset.is_none() {
92            if let Some(limit_expr) = &stmt.limit {
93                let has_partition_by = window_functions
94                    .iter()
95                    .any(|wf| !wf.partition_by_exprs.is_empty());
96
97                if has_partition_by && Self::all_partitions_match(&window_functions) {
98                    if let Expression::IntegerLiteral(lit) = limit_expr.as_ref() {
99                        let limit_val = lit.value;
100                        if limit_val > 0 {
101                            return self.execute_select_with_window_functions_streaming(
102                                stmt,
103                                ctx,
104                                base_rows,
105                                base_columns,
106                                &window_functions,
107                                limit_val as usize,
108                            );
109                        }
110                    }
111                }
112            }
113        }
114
115        // Build column index map for base columns
116        let mut col_index_map = build_column_index_map(base_columns);
117
118        // Build a mapping from aggregate expression patterns to their column names
119        // This handles cases like:
120        // - SUM(val) AS grp_sum -> maps "sum(val)" to column index of "grp_sum"
121        // - COALESCE(SUM(val), 0) AS total -> maps "sum(val)" to column index of "total"
122        for col_expr in stmt.columns.iter() {
123            if let Expression::Aliased(aliased) = col_expr {
124                let alias_lower = aliased.alias.value_lower.as_str();
125                if let Some(&idx) = col_index_map.get(alias_lower) {
126                    // Extract all aggregate patterns from this expression (including nested ones)
127                    let patterns = self.extract_aggregate_patterns(aliased.expression.as_ref());
128                    for pattern in patterns {
129                        col_index_map.insert(pattern.to_lowercase(), idx);
130                    }
131                }
132            }
133        }
134
135        // Step 1: Compute all window function values upfront
136        // OPTIMIZATION: Use FxHashMap for fastest lookups with trusted keys
137
138        // OPTIMIZATION: Precompute ORDER BY values ONCE for each unique ORDER BY clause
139        // This avoids redundant computation when multiple window functions share the same ORDER BY
140        // We use a Vec for the cache since the number of unique ORDER BY clauses is typically small
141        // NOTE: We use string representation for semantic comparison because PartialEq on expressions
142        // compares token positions, making structurally identical expressions from different window
143        // functions appear different.
144        let mut order_by_cache: Vec<(String, ColumnarOrderByValues)> = Vec::new();
145        for wf in &window_functions {
146            if !wf.order_by.is_empty() {
147                // Create a semantic key from the ORDER BY expressions (ignores token positions)
148                let cache_key = Self::order_by_cache_key(&wf.order_by);
149                // Check if this ORDER BY clause is already in the cache
150                let already_cached = order_by_cache.iter().any(|(key, _)| key == &cache_key);
151                if !already_cached {
152                    let precomputed = self.precompute_order_by_values(
153                        &wf.order_by,
154                        base_rows,
155                        base_columns,
156                        &col_index_map,
157                        ctx,
158                    )?;
159                    order_by_cache.push((cache_key, precomputed));
160                }
161            }
162        }
163
164        let mut window_value_map: StringMap<Vec<Value>> = StringMap::new();
165        for wf in &window_functions {
166            let window_values = self.compute_window_function(
167                wf,
168                base_rows,
169                base_columns,
170                &col_index_map,
171                ctx,
172                pre_sorted.as_ref(),
173                pre_grouped.as_ref(),
174                &order_by_cache,
175            )?;
176            window_value_map.insert(wf.column_name.to_lowercase(), window_values);
177        }
178
179        // Step 2: Build output columns and rows based on the SELECT list
180        // The result should respect the SELECT list order, not just append window functions
181        let mut result_columns = Vec::new();
182
183        // Parse the SELECT list to determine output column order
184        let select_items = self.parse_select_list_for_window(stmt, base_columns, &window_functions);
185
186        for item in &select_items {
187            result_columns.push(item.output_name.clone());
188        }
189
190        // Step 3: Build result using COLUMNAR storage
191        // OPTIMIZATION: Instead of allocating one Row per result row, we store data column-major
192        // and use ColumnarResult which materializes rows lazily with a single reused buffer.
193        // This reduces allocations from O(num_rows) to O(num_columns).
194        let num_rows = base_rows.len();
195
196        // Build aliases from col_index_map for expression evaluation
197        let agg_aliases: Vec<(String, usize)> =
198            col_index_map.iter().map(|(k, v)| (k.clone(), *v)).collect();
199
200        // Pre-transform expressions with window functions by replacing Window expr with Identifier
201        // This is done once, not per row
202        let transformed_items: Vec<_> = select_items
203            .iter()
204            .map(|item| match &item.source {
205                SelectItemSource::ExpressionWithWindow(expr, wf_names) => {
206                    let mut counter = 0;
207                    let transformed =
208                        Self::replace_windows_with_identifiers(expr, wf_names, &mut counter);
209                    (item, Some(transformed), Some(wf_names.clone()))
210                }
211                _ => (item, None, None),
212            })
213            .collect();
214
215        // Build extended columns (base_columns + synthetic window columns)
216        let mut extended_columns = base_columns.to_vec();
217        let mut added_wf_names: Vec<String> = Vec::new();
218        for (_, _, wf_names_opt) in &transformed_items {
219            if let Some(wf_names) = wf_names_opt {
220                for wf_name in wf_names {
221                    if !added_wf_names.contains(wf_name) {
222                        extended_columns.push(wf_name.clone());
223                        added_wf_names.push(wf_name.clone());
224                    }
225                }
226            }
227        }
228
229        // Collect Expression items (with base_columns) and their indices
230        let base_expr_items: Vec<(usize, &Expression)> = transformed_items
231            .iter()
232            .enumerate()
233            .filter_map(|(i, (item, _, _))| {
234                if let SelectItemSource::Expression(expr) = &item.source {
235                    Some((i, expr))
236                } else {
237                    None
238                }
239            })
240            .collect();
241
242        // Collect ExpressionWithWindow transformed expressions and their indices
243        let ext_expr_items: Vec<(usize, &Expression)> = transformed_items
244            .iter()
245            .enumerate()
246            .filter_map(|(i, (_, transformed_opt, _))| transformed_opt.as_ref().map(|t| (i, t)))
247            .collect();
248
249        // Pre-compile base expressions (Expression items with base_columns)
250        // CRITICAL: Propagate compilation errors instead of silently producing NULLs
251        let base_exprs: Vec<Expression> =
252            base_expr_items.iter().map(|(_, e)| (*e).clone()).collect();
253        let mut base_eval = if !base_exprs.is_empty() {
254            Some(
255                MultiExpressionEval::compile_with_aliases(&base_exprs, base_columns, &agg_aliases)?
256                    .with_context(ctx),
257            )
258        } else {
259            None
260        };
261
262        // Pre-compile extended expressions (ExpressionWithWindow with extended_columns)
263        // CRITICAL: Propagate compilation errors instead of silently producing NULLs
264        let ext_exprs: Vec<Expression> = ext_expr_items.iter().map(|(_, e)| (*e).clone()).collect();
265        let mut ext_eval = if !ext_exprs.is_empty() {
266            Some(
267                MultiExpressionEval::compile_with_aliases(
268                    &ext_exprs,
269                    &extended_columns,
270                    &agg_aliases,
271                )?
272                .with_context(ctx),
273            )
274        } else {
275            None
276        };
277
278        // OPTIMIZATION: Pre-allocate ext_values buffer for extended expressions
279        let ext_values_capacity = if !ext_expr_items.is_empty() {
280            base_rows.first().map_or(0, |r| r.1.len()) + added_wf_names.len()
281        } else {
282            0
283        };
284        let mut ext_values: CompactVec<Value> = CompactVec::with_capacity(ext_values_capacity);
285
286        // Number of output columns
287        let num_items = select_items.len();
288
289        // COLUMNAR STORAGE OPTIMIZATION: Build columns in column-major order
290        // This is more cache-efficient than row-by-row iteration and enables
291        // moving window function Vecs directly (zero-copy for window results).
292        //
293        // Phase 1: Build non-expression columns (WindowFunction, BaseColumn)
294        // Phase 2: Fill expression columns row-by-row (requires evaluation)
295        let mut column_data: Vec<Vec<Value>> = Vec::with_capacity(num_items);
296
297        // Track which window functions are used in expressions (need to keep them)
298        let wf_names_in_exprs: std::collections::HashSet<&str> =
299            added_wf_names.iter().map(|s| s.as_str()).collect();
300
301        // Phase 1: Build columns for WindowFunction and BaseColumn items
302        // Expression columns get placeholder Vecs (filled in Phase 2)
303        for (item, _, _) in &transformed_items {
304            match &item.source {
305                SelectItemSource::WindowFunction(wf_name_lower) => {
306                    let source_values = window_value_map.get(wf_name_lower).ok_or_else(|| {
307                        Error::internal(format!(
308                            "window projection source {wf_name_lower} is missing"
309                        ))
310                    })?;
311                    if source_values.len() != num_rows {
312                        return Err(Error::internal(format!(
313                            "window projection source {wf_name_lower} has {} rows, expected {num_rows}",
314                            source_values.len()
315                        )));
316                    }
317                    // OPTIMIZATION: Move or clone the entire Vec at once
318                    // If this window function is also used in an expression, we need to keep it
319                    if wf_names_in_exprs.contains(wf_name_lower.as_str()) {
320                        // Clone the entire Vec (more cache-efficient than element-by-element)
321                        let values = source_values.clone();
322                        column_data.push(values);
323                    } else {
324                        // Move the Vec directly (zero-copy)
325                        let values = window_value_map.remove(wf_name_lower).ok_or_else(|| {
326                            Error::internal(format!(
327                                "window projection source {wf_name_lower} disappeared"
328                            ))
329                        })?;
330                        column_data.push(values);
331                    }
332                }
333                SelectItemSource::BaseColumn(base_col_idx) => {
334                    // OPTIMIZATION: Build base column in one pass (column-wise)
335                    let mut values = Vec::with_capacity(num_rows);
336                    for (_, base_row) in base_rows {
337                        values.push(base_row.get(*base_col_idx).cloned().ok_or_else(|| {
338                            Error::internal(format!(
339                                "window base projection index {base_col_idx} is outside row width {}",
340                                base_row.len()
341                            ))
342                        })?);
343                    }
344                    column_data.push(values);
345                }
346                SelectItemSource::Expression(_) | SelectItemSource::ExpressionWithWindow(_, _) => {
347                    // Placeholder - will be filled in Phase 2
348                    column_data.push(vec![NULL_VALUE; num_rows]);
349                }
350            }
351        }
352
353        // Phase 2: Fill expression columns row-by-row (requires evaluation)
354        let has_expressions = !base_expr_items.is_empty() || !ext_expr_items.is_empty();
355        if has_expressions {
356            for (row_idx, (_, base_row)) in base_rows.iter().enumerate() {
357                // Evaluate base expressions and update their column values
358                if let Some(ref mut eval) = base_eval {
359                    let base_results = eval.eval_all(base_row)?;
360                    if base_results.len() != base_expr_items.len() {
361                        return Err(Error::internal(format!(
362                            "window base projection produced {} values, expected {}",
363                            base_results.len(),
364                            base_expr_items.len()
365                        )));
366                    }
367                    for (eval_idx, (item_idx, _)) in base_expr_items.iter().enumerate() {
368                        column_data[*item_idx][row_idx] = base_results[eval_idx].clone();
369                    }
370                }
371
372                // Evaluate extended expressions (if any) and update their column values
373                if !ext_expr_items.is_empty() {
374                    if let Some(ref mut eval) = ext_eval {
375                        // Reuse ext_values buffer: clear and refill
376                        ext_values.clear();
377                        ext_values.extend(base_row.iter().cloned());
378                        for wf_name in &added_wf_names {
379                            let wf_value = window_value_map
380                                .get(wf_name)
381                                .and_then(|vals| vals.get(row_idx).cloned())
382                                .ok_or_else(|| {
383                                    Error::internal(format!(
384                                        "window expression source {wf_name} is missing row {row_idx}"
385                                    ))
386                                })?;
387                            ext_values.push(wf_value);
388                        }
389                        let ext_row = Row::from_compact_vec(ext_values.clone());
390
391                        let ext_result_values = eval.eval_all(&ext_row)?;
392                        if ext_result_values.len() != ext_expr_items.len() {
393                            return Err(Error::internal(format!(
394                                "window extended projection produced {} values, expected {}",
395                                ext_result_values.len(),
396                                ext_expr_items.len()
397                            )));
398                        }
399                        for (eval_idx, (item_idx, _)) in ext_expr_items.iter().enumerate() {
400                            column_data[*item_idx][row_idx] = ext_result_values[eval_idx].clone();
401                        }
402                    }
403                }
404            }
405        }
406
407        self.append_hidden_window_order_columns(
408            stmt,
409            ctx,
410            base_rows,
411            base_columns,
412            &mut result_columns,
413            &mut column_data,
414        )?;
415
416        // Return ColumnarResult which materializes rows lazily with zero per-row allocation
417        Ok(Box::new(ColumnarResult::new(result_columns, column_data)))
418    }
419
420    /// Preserve source expressions required by top-level ORDER BY / DISTINCT ON
421    /// after the window SELECT projection. These columns are internal: the outer
422    /// executor consumes them for sorting/deduplication and then truncates the
423    /// public row back to the SELECT width.
424    pub(super) fn append_hidden_window_order_columns(
425        &self,
426        stmt: &SelectStatement,
427        ctx: &ExecutionContext,
428        base_rows: &[(i64, Row)],
429        base_columns: &[String],
430        result_columns: &mut Vec<String>,
431        column_data: &mut Vec<Vec<Value>>,
432    ) -> Result<()> {
433        if stmt.order_by.is_empty() && stmt.distinct_on.is_empty() {
434            return Ok(());
435        }
436
437        let base_index = build_column_index_map(base_columns);
438        let mut append_expression = |expression: &Expression| -> Result<()> {
439            // The outer ORDER BY mapper already resolves output aliases and
440            // expressions that are present in the SELECT list.
441            let selected = stmt.columns.iter().any(|selected| match selected {
442                Expression::Aliased(aliased) => {
443                    aliased.expression.as_ref().to_string() == expression.to_string()
444                        || matches!(expression, Expression::Identifier(id)
445                            if aliased.alias.value_lower == id.value_lower)
446                }
447                other => other.to_string() == expression.to_string(),
448            });
449            if selected || matches!(expression, Expression::IntegerLiteral(_)) {
450                return Ok(());
451            }
452
453            let (column_name, values) = match expression {
454                Expression::Identifier(id) => {
455                    let Some(&index) = base_index.get(id.value_lower.as_str()) else {
456                        // It may be a SELECT alias; the outer mapper will resolve it.
457                        if result_columns
458                            .iter()
459                            .any(|name| name.eq_ignore_ascii_case(id.value_lower.as_str()))
460                        {
461                            return Ok(());
462                        }
463                        return Err(Error::ColumnNotFound(id.value.to_string()));
464                    };
465                    let name = base_columns[index].clone();
466                    let values = base_rows
467                        .iter()
468                        .map(|(_, row)| row.get(index).cloned().unwrap_or(NULL_VALUE))
469                        .collect();
470                    (name, values)
471                }
472                Expression::QualifiedIdentifier(id) => {
473                    let qualified = format!("{}.{}", id.qualifier.value_lower, id.name.value_lower);
474                    let Some(&index) = base_index
475                        .get(qualified.as_str())
476                        .or_else(|| base_index.get(id.name.value_lower.as_str()))
477                    else {
478                        return Err(Error::ColumnNotFound(format!(
479                            "{}.{}",
480                            id.qualifier.value, id.name.value
481                        )));
482                    };
483                    let name = base_columns[index].clone();
484                    let values = base_rows
485                        .iter()
486                        .map(|(_, row)| row.get(index).cloned().unwrap_or(NULL_VALUE))
487                        .collect();
488                    (name, values)
489                }
490                _ => {
491                    let name = expression.to_string();
492                    if result_columns
493                        .iter()
494                        .any(|column| column.eq_ignore_ascii_case(&name))
495                    {
496                        return Ok(());
497                    }
498                    let mut evaluator =
499                        ExpressionEval::compile(expression, base_columns)?.with_context(ctx);
500                    let values = base_rows
501                        .iter()
502                        .map(|(_, row)| evaluator.eval(row))
503                        .collect::<Result<Vec<_>>>()?;
504                    (name, values)
505                }
506            };
507
508            if !result_columns
509                .iter()
510                .any(|name| name.eq_ignore_ascii_case(&column_name))
511            {
512                result_columns.push(column_name);
513                column_data.push(values);
514            }
515            Ok(())
516        };
517
518        for order in &stmt.order_by {
519            append_expression(&order.expression)?;
520        }
521        for expression in &stmt.distinct_on {
522            append_expression(expression)?;
523        }
524        Ok(())
525    }
526
527    /// Streaming execution for window functions with LIMIT pushdown
528    /// Processes partitions one at a time and stops early when LIMIT is reached
529    pub(super) fn execute_select_with_window_functions_streaming(
530        &self,
531        stmt: &SelectStatement,
532        ctx: &ExecutionContext,
533        base_rows: &[(i64, Row)],
534        base_columns: &[String],
535        window_functions: &[WindowFunctionInfo],
536        limit: usize,
537    ) -> Result<Box<dyn QueryResult>> {
538        // Use the first window function with PARTITION BY for partitioning
539        let primary_wf = window_functions
540            .iter()
541            .find(|wf| !wf.partition_by_exprs.is_empty())
542            .unwrap(); // Safe: we checked has_partition_by before calling this
543
544        // Build column index map
545        let col_index_map = build_column_index_map(base_columns);
546
547        // Build partition map from the primary window function
548        let partitions =
549            Self::build_partition_map(primary_wf, base_rows, base_columns, &col_index_map, ctx)?;
550
551        // Build result columns from SELECT list
552        let select_items = self.parse_select_list_for_window(stmt, base_columns, window_functions);
553        let result_columns: Vec<String> =
554            select_items.iter().map(|i| i.output_name.clone()).collect();
555
556        // Precompute ORDER BY values once for each unique ORDER BY clause
557        let mut order_by_cache: Vec<(String, ColumnarOrderByValues)> = Vec::new();
558        for wf in window_functions {
559            if !wf.order_by.is_empty() {
560                let cache_key = Self::order_by_cache_key(&wf.order_by);
561                let already_cached = order_by_cache.iter().any(|(key, _)| key == &cache_key);
562                if !already_cached {
563                    let precomputed = self.precompute_order_by_values(
564                        &wf.order_by,
565                        base_rows,
566                        base_columns,
567                        &col_index_map,
568                        ctx,
569                    )?;
570                    order_by_cache.push((cache_key, precomputed));
571                }
572            }
573        }
574
575        // Resolve window functions from registry
576        let resolved_wfs: Vec<_> = window_functions
577            .iter()
578            .map(|wf| {
579                let is_agg = self.host.window_function_registry().is_aggregate(&wf.name);
580                let func = if is_agg {
581                    None
582                } else {
583                    self.host.window_function_registry().get_window(&wf.name)
584                };
585                (wf, func, is_agg)
586            })
587            .collect();
588
589        // Precompute aggregate window functions once over all rows (not per partition)
590        let mut precomputed_agg: StringMap<Vec<Value>> = StringMap::new();
591        for (wf, _, is_agg) in &resolved_wfs {
592            if *is_agg {
593                let cache_key = Self::order_by_cache_key(&wf.order_by);
594                let precomputed_order_by = order_by_cache
595                    .iter()
596                    .find(|(key, _)| key == &cache_key)
597                    .map(|(_, v)| v);
598                let agg_results = self.compute_aggregate_window_function(
599                    wf,
600                    base_rows,
601                    base_columns,
602                    &col_index_map,
603                    ctx,
604                    None,
605                    precomputed_order_by,
606                )?;
607                precomputed_agg.insert(wf.column_name.to_lowercase(), agg_results);
608            }
609        }
610
611        // Process partitions one at a time, stopping when we have enough rows
612        let mut result_rows = RowVec::with_capacity(limit);
613        let mut result_row_id = 0i64;
614
615        let partitions_vec: Vec<_> = partitions.into_iter().collect();
616
617        for (_partition_key, row_indices) in partitions_vec {
618            if result_rows.len() >= limit {
619                break;
620            }
621
622            // Compute window functions for this partition.
623            let mut window_value_map: StringMap<Vec<Value>> = StringMap::new();
624
625            for (wf, win_func_opt, is_agg) in &resolved_wfs {
626                if *is_agg {
627                    // Slice precomputed aggregate results for this partition
628                    let key = wf.column_name.to_lowercase();
629                    if let Some(all_results) = precomputed_agg.get(&key) {
630                        let partition_vals: Vec<Value> = row_indices
631                            .iter()
632                            .map(|&i| all_results[i].clone())
633                            .collect();
634                        window_value_map.insert(key, partition_vals);
635                    }
636                } else if let Some(win_func) = win_func_opt {
637                    let cache_key = Self::order_by_cache_key(&wf.order_by);
638                    let precomputed_order_by = order_by_cache
639                        .iter()
640                        .find(|(key, _)| key == &cache_key)
641                        .map(|(_, v)| v);
642                    let (sorted_values, sorted_indices) = self.compute_window_for_partition(
643                        win_func.as_ref(),
644                        wf,
645                        base_rows,
646                        row_indices.clone(),
647                        precomputed_order_by,
648                        base_columns,
649                        &col_index_map,
650                        ctx,
651                        false,
652                    )?;
653                    // Remap: sorted_values[pos] corresponds to row sorted_indices[pos].
654                    // Build a vec indexed by position within row_indices using an O(n) index map.
655                    let orig_to_local: FxHashMap<usize, usize> = row_indices
656                        .iter()
657                        .enumerate()
658                        .map(|(local, &orig)| (orig, local))
659                        .collect();
660                    let mut by_orig = vec![NULL_VALUE; row_indices.len()];
661                    for (pos, &orig_idx) in sorted_indices.iter().enumerate() {
662                        if let Some(&local) = orig_to_local.get(&orig_idx) {
663                            by_orig[local] = sorted_values[pos].clone();
664                        }
665                    }
666                    window_value_map.insert(wf.column_name.to_lowercase(), by_orig);
667                }
668            }
669
670            // Precompile expression evaluators once per partition (not per row)
671            enum CompiledItem<'a> {
672                BaseColumn(usize),
673                WindowFunction(&'a str),
674                Expression(ExpressionEval),
675                ExpressionWithWindow(ExpressionEval, Vec<&'a str>),
676            }
677            let num_items = select_items.len();
678            let mut compiled_items: Vec<CompiledItem<'_>> = Vec::with_capacity(num_items);
679            for item in &select_items {
680                compiled_items.push(match &item.source {
681                    SelectItemSource::BaseColumn(idx) => CompiledItem::BaseColumn(*idx),
682                    SelectItemSource::WindowFunction(name) => {
683                        CompiledItem::WindowFunction(name.as_str())
684                    }
685                    SelectItemSource::Expression(expr) => {
686                        CompiledItem::Expression(ExpressionEval::compile(expr, base_columns)?)
687                    }
688                    SelectItemSource::ExpressionWithWindow(expr, wf_names) => {
689                        let mut counter = 0;
690                        let transformed =
691                            Self::replace_windows_with_identifiers(expr, wf_names, &mut counter);
692                        let mut ext_columns = base_columns.to_vec();
693                        for wf_name in wf_names {
694                            ext_columns.push(wf_name.clone());
695                        }
696                        let eval = ExpressionEval::compile(&transformed, &ext_columns)?;
697                        let name_refs: Vec<&str> = wf_names.iter().map(|s| s.as_str()).collect();
698                        CompiledItem::ExpressionWithWindow(eval, name_refs)
699                    }
700                });
701            }
702
703            let ext_capacity = base_rows.first().map_or(0, |r| r.1.len()) + 1;
704            let mut ext_values: CompactVec<Value> = CompactVec::with_capacity(ext_capacity);
705
706            // Output rows in partition order (row_indices order)
707            for (local_pos, &orig_idx) in row_indices.iter().enumerate() {
708                if result_rows.len() >= limit {
709                    break;
710                }
711
712                let base_row = &base_rows[orig_idx].1;
713                let mut values: CompactVec<Value> = CompactVec::with_capacity(num_items);
714
715                for ci in &mut compiled_items {
716                    let value = match ci {
717                        CompiledItem::BaseColumn(col_idx) => {
718                            base_row.get(*col_idx).cloned().unwrap_or(NULL_VALUE)
719                        }
720                        CompiledItem::WindowFunction(wf_name_lower) => window_value_map
721                            .get(*wf_name_lower)
722                            .and_then(|vals| vals.get(local_pos).cloned())
723                            .unwrap_or(NULL_VALUE),
724                        CompiledItem::Expression(eval) => eval.eval(base_row)?,
725                        CompiledItem::ExpressionWithWindow(eval, wf_name_refs) => {
726                            ext_values.clear();
727                            ext_values.extend(base_row.iter().cloned());
728                            for wf_name in wf_name_refs.iter() {
729                                let wf_value = window_value_map
730                                    .get(*wf_name)
731                                    .and_then(|vals| vals.get(local_pos).cloned())
732                                    .unwrap_or(NULL_VALUE);
733                                ext_values.push(wf_value);
734                            }
735                            let ext_row = Row::from_compact_vec(ext_values.clone());
736                            eval.eval(&ext_row)?
737                        }
738                    };
739                    values.push(value);
740                }
741                result_rows.push((result_row_id, Row::from_compact_vec(values)));
742                result_row_id += 1;
743            }
744        }
745
746        Ok(Box::new(ExecutorResult::new(result_columns, result_rows)))
747    }
748
749    /// Lazy partition fetching for window functions with LIMIT pushdown
750    /// Fetches partitions one at a time from the index and stops when LIMIT is reached
751    /// This is the key optimization for PARTITION BY + LIMIT queries
752    pub fn execute_select_with_window_functions_lazy_partition(
753        &self,
754        stmt: &SelectStatement,
755        ctx: &ExecutionContext,
756        table: &dyn Table,
757        base_columns: &[String],
758        partition_col: &str,
759        limit: usize,
760    ) -> Result<Box<dyn QueryResult>> {
761        // Parse window functions from SELECT list
762        let window_functions = self.parse_window_functions(stmt, base_columns)?;
763        if window_functions.is_empty() {
764            return Err(Error::internal(
765                "No window functions found for lazy partition fetch",
766            ));
767        }
768
769        // Build column index map
770        let col_index_map = build_column_index_map(base_columns);
771
772        // Resolve window functions from registry
773        let resolved_wfs: Vec<_> = window_functions
774            .iter()
775            .map(|wf| {
776                let is_agg = self.host.window_function_registry().is_aggregate(&wf.name);
777                let func = if is_agg {
778                    None
779                } else {
780                    self.host.window_function_registry().get_window(&wf.name)
781                };
782                (wf, func, is_agg)
783            })
784            .collect();
785
786        // Build result columns from SELECT list
787        let select_items = self.parse_select_list_for_window(stmt, base_columns, &window_functions);
788        let result_columns: Vec<String> =
789            select_items.iter().map(|i| i.output_name.clone()).collect();
790
791        // Get partition values from the index (lazy iteration key!)
792        let partition_values = match table.get_partition_values(partition_col) {
793            Some(values) => values,
794            None => return Err(Error::internal("Failed to get partition values from index")),
795        };
796
797        // Process partitions one at a time, stopping when we have enough rows
798        let mut result_rows = RowVec::with_capacity(limit);
799        let mut result_row_id = 0i64;
800
801        for partition_value in partition_values {
802            if result_rows.len() >= limit {
803                break;
804            }
805
806            // Fetch rows for this partition only (KEY OPTIMIZATION!)
807            let partition_rows =
808                match table.get_rows_for_partition_value(partition_col, &partition_value) {
809                    Some(rows) => rows,
810                    None => continue,
811                };
812
813            if partition_rows.is_empty() {
814                continue;
815            }
816
817            // Precompute ORDER BY values for each unique ORDER BY clause
818            let mut order_by_cache: Vec<(String, ColumnarOrderByValues)> = Vec::new();
819            for wf in &window_functions {
820                if !wf.order_by.is_empty() {
821                    let cache_key = Self::order_by_cache_key(&wf.order_by);
822                    let already_cached = order_by_cache.iter().any(|(key, _)| key == &cache_key);
823                    if !already_cached {
824                        let precomputed = self.precompute_order_by_values(
825                            &wf.order_by,
826                            &partition_rows,
827                            base_columns,
828                            &col_index_map,
829                            ctx,
830                        )?;
831                        order_by_cache.push((cache_key, precomputed));
832                    }
833                }
834            }
835
836            // Compute ALL window functions for this partition.
837            // Values are stored keyed by local position within row_indices.
838            let row_indices: Vec<usize> = (0..partition_rows.len()).collect();
839            let mut window_value_map: StringMap<Vec<Value>> = StringMap::new();
840
841            for (wf, win_func_opt, is_agg) in &resolved_wfs {
842                let cache_key = Self::order_by_cache_key(&wf.order_by);
843                let precomputed_order_by = order_by_cache
844                    .iter()
845                    .find(|(key, _)| key == &cache_key)
846                    .map(|(_, v)| v);
847
848                if *is_agg {
849                    let agg_results = self.compute_aggregate_window_function(
850                        wf,
851                        &partition_rows,
852                        base_columns,
853                        &col_index_map,
854                        ctx,
855                        None,
856                        precomputed_order_by,
857                    )?;
858                    // agg_results is already indexed by local row index
859                    window_value_map.insert(wf.column_name.to_lowercase(), agg_results);
860                } else if let Some(win_func) = win_func_opt {
861                    let (sorted_values, sorted_indices) = self.compute_window_for_partition(
862                        win_func.as_ref(),
863                        wf,
864                        &partition_rows,
865                        row_indices.clone(),
866                        precomputed_order_by,
867                        base_columns,
868                        &col_index_map,
869                        ctx,
870                        false,
871                    )?;
872                    // Remap from sorted order to local row order
873                    let mut by_local = vec![NULL_VALUE; row_indices.len()];
874                    for (pos, &local_idx) in sorted_indices.iter().enumerate() {
875                        by_local[local_idx] = sorted_values[pos].clone();
876                    }
877                    window_value_map.insert(wf.column_name.to_lowercase(), by_local);
878                }
879            }
880
881            // Precompile expression evaluators once per partition (not per row)
882            enum CompiledItemLazy<'a> {
883                BaseColumn(usize),
884                WindowFunction(&'a str),
885                Expression(ExpressionEval),
886                ExpressionWithWindow(ExpressionEval, Vec<&'a str>),
887            }
888            let num_items = select_items.len();
889            let mut compiled_items: Vec<CompiledItemLazy<'_>> = Vec::with_capacity(num_items);
890            for item in &select_items {
891                compiled_items.push(match &item.source {
892                    SelectItemSource::BaseColumn(idx) => CompiledItemLazy::BaseColumn(*idx),
893                    SelectItemSource::WindowFunction(name) => {
894                        CompiledItemLazy::WindowFunction(name.as_str())
895                    }
896                    SelectItemSource::Expression(expr) => {
897                        CompiledItemLazy::Expression(ExpressionEval::compile(expr, base_columns)?)
898                    }
899                    SelectItemSource::ExpressionWithWindow(expr, wf_names) => {
900                        let mut counter = 0;
901                        let transformed =
902                            Self::replace_windows_with_identifiers(expr, wf_names, &mut counter);
903                        let mut ext_columns = base_columns.to_vec();
904                        for wf_name in wf_names {
905                            ext_columns.push(wf_name.clone());
906                        }
907                        let eval = ExpressionEval::compile(&transformed, &ext_columns)?;
908                        let name_refs: Vec<&str> = wf_names.iter().map(|s| s.as_str()).collect();
909                        CompiledItemLazy::ExpressionWithWindow(eval, name_refs)
910                    }
911                });
912            }
913
914            let mut ext_values: CompactVec<Value> =
915                CompactVec::with_capacity(partition_rows.first().map_or(0, |r| r.1.len()) + 1);
916
917            // Output rows in partition order
918            for (local_pos, &row_idx) in row_indices.iter().enumerate() {
919                if result_rows.len() >= limit {
920                    break;
921                }
922
923                let (_, base_row) = &partition_rows[row_idx];
924                let mut values: CompactVec<Value> = CompactVec::with_capacity(num_items);
925
926                for ci in &mut compiled_items {
927                    let val = match ci {
928                        CompiledItemLazy::BaseColumn(col_idx) => {
929                            base_row.get(*col_idx).cloned().unwrap_or(NULL_VALUE)
930                        }
931                        CompiledItemLazy::WindowFunction(wf_name_lower) => window_value_map
932                            .get(*wf_name_lower)
933                            .and_then(|vals| vals.get(local_pos).cloned())
934                            .unwrap_or(NULL_VALUE),
935                        CompiledItemLazy::Expression(eval) => eval.eval(base_row)?,
936                        CompiledItemLazy::ExpressionWithWindow(eval, wf_name_refs) => {
937                            ext_values.clear();
938                            ext_values.extend(base_row.iter().cloned());
939                            for wf_name in wf_name_refs.iter() {
940                                let wf_value = window_value_map
941                                    .get(*wf_name)
942                                    .and_then(|vals| vals.get(local_pos).cloned())
943                                    .unwrap_or(NULL_VALUE);
944                                ext_values.push(wf_value);
945                            }
946                            let ext_row = Row::from_compact_vec(ext_values.clone());
947                            eval.eval(&ext_row)?
948                        }
949                    };
950                    values.push(val);
951                }
952                result_rows.push((result_row_id, Row::from_compact_vec(values)));
953                result_row_id += 1;
954            }
955        }
956
957        Ok(Box::new(ExecutorResult::new(result_columns, result_rows)))
958    }
959}