Skip to main content

uqa_sql/semantics/sets/
rewrite.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Set-returning and aggregate dependency expression rewrites.
8
9use crate::plan::{ProjectionPlan, QueryBlockPlan};
10use crate::{FunctionTypeResolver, RowSchema, ScalarExpr, ScalarFrameBound};
11use crate::{SQLError, SQLParam};
12use uqa_core::Value;
13
14use super::validation::{
15    expression_may_return_set, function_may_return_set, resolve_set_function_binding,
16};
17use super::{
18    AggregateOutputProjectionPlan, GroupSetProjectionPlan, ProjectionTarget, SetFunctionCall,
19    SetFunctionCatalog,
20};
21use crate::semantics::aggregates::{exprs_match, is_aggregate};
22use crate::semantics::projection_columns;
23
24#[expect(
25    clippy::too_many_lines,
26    reason = "preserves SELECT schema and row identity"
27)]
28pub fn rewrite_set_calls(
29    engine: &dyn SetFunctionCatalog,
30    resolver: &dyn FunctionTypeResolver,
31    mut expression: ScalarExpr,
32    calls: &mut Vec<SetFunctionCall>,
33    call_relation: crate::ast::InternalRelationId,
34    schema: &RowSchema,
35    params: &[SQLParam],
36) -> Result<ScalarExpr, SQLError> {
37    let descendant_start = calls.len();
38    match &mut expression {
39        ScalarExpr::Func {
40            args,
41            order_by,
42            filter,
43            ..
44        } => {
45            for argument in args {
46                *argument = rewrite_set_calls(
47                    engine,
48                    resolver,
49                    argument.clone(),
50                    calls,
51                    call_relation,
52                    schema,
53                    params,
54                )?;
55            }
56            for order in order_by {
57                order.expr = rewrite_set_calls(
58                    engine,
59                    resolver,
60                    order.expr.clone(),
61                    calls,
62                    call_relation,
63                    schema,
64                    params,
65                )?;
66            }
67            if let Some(filter) = filter {
68                **filter = rewrite_set_calls(
69                    engine,
70                    resolver,
71                    (**filter).clone(),
72                    calls,
73                    call_relation,
74                    schema,
75                    params,
76                )?;
77            }
78        }
79        ScalarExpr::Array(items)
80        | ScalarExpr::Row(items)
81        | ScalarExpr::CompositeRow { items, .. }
82        | ScalarExpr::And(items)
83        | ScalarExpr::Or(items) => {
84            for item in items {
85                *item = rewrite_set_calls(
86                    engine,
87                    resolver,
88                    item.clone(),
89                    calls,
90                    call_relation,
91                    schema,
92                    params,
93                )?;
94            }
95        }
96        ScalarExpr::Binary { lhs, rhs, .. } => {
97            **lhs = rewrite_set_calls(
98                engine,
99                resolver,
100                (**lhs).clone(),
101                calls,
102                call_relation,
103                schema,
104                params,
105            )?;
106            **rhs = rewrite_set_calls(
107                engine,
108                resolver,
109                (**rhs).clone(),
110                calls,
111                call_relation,
112                schema,
113                params,
114            )?;
115        }
116        ScalarExpr::Not(inner)
117        | ScalarExpr::UnaryMinus(inner)
118        | ScalarExpr::IsNull { expr: inner, .. }
119        | ScalarExpr::Cast { expr: inner, .. } => {
120            **inner = rewrite_set_calls(
121                engine,
122                resolver,
123                (**inner).clone(),
124                calls,
125                call_relation,
126                schema,
127                params,
128            )?;
129        }
130        ScalarExpr::Between { expr, low, high } => {
131            **expr = rewrite_set_calls(
132                engine,
133                resolver,
134                (**expr).clone(),
135                calls,
136                call_relation,
137                schema,
138                params,
139            )?;
140            **low = rewrite_set_calls(
141                engine,
142                resolver,
143                (**low).clone(),
144                calls,
145                call_relation,
146                schema,
147                params,
148            )?;
149            **high = rewrite_set_calls(
150                engine,
151                resolver,
152                (**high).clone(),
153                calls,
154                call_relation,
155                schema,
156                params,
157            )?;
158        }
159        ScalarExpr::InList { expr, list, .. } => {
160            **expr = rewrite_set_calls(
161                engine,
162                resolver,
163                (**expr).clone(),
164                calls,
165                call_relation,
166                schema,
167                params,
168            )?;
169            for item in list {
170                *item = rewrite_set_calls(
171                    engine,
172                    resolver,
173                    item.clone(),
174                    calls,
175                    call_relation,
176                    schema,
177                    params,
178                )?;
179            }
180        }
181        ScalarExpr::WindowCall {
182            args, spec, filter, ..
183        } => {
184            for argument in args {
185                *argument = rewrite_set_calls(
186                    engine,
187                    resolver,
188                    argument.clone(),
189                    calls,
190                    call_relation,
191                    schema,
192                    params,
193                )?;
194            }
195            if let Some(filter) = filter {
196                **filter = rewrite_set_calls(
197                    engine,
198                    resolver,
199                    (**filter).clone(),
200                    calls,
201                    call_relation,
202                    schema,
203                    params,
204                )?;
205            }
206            for item in &mut spec.partition_by {
207                *item = rewrite_set_calls(
208                    engine,
209                    resolver,
210                    item.clone(),
211                    calls,
212                    call_relation,
213                    schema,
214                    params,
215                )?;
216            }
217            for order in &mut spec.order_by {
218                order.expr = rewrite_set_calls(
219                    engine,
220                    resolver,
221                    order.expr.clone(),
222                    calls,
223                    call_relation,
224                    schema,
225                    params,
226                )?;
227            }
228            if let Some(frame) = &mut spec.frame {
229                rewrite_set_frame_bound(
230                    engine,
231                    resolver,
232                    &mut frame.start,
233                    calls,
234                    call_relation,
235                    schema,
236                    params,
237                )?;
238                rewrite_set_frame_bound(
239                    engine,
240                    resolver,
241                    &mut frame.end,
242                    calls,
243                    call_relation,
244                    schema,
245                    params,
246                )?;
247            }
248        }
249        ScalarExpr::Case {
250            base,
251            when,
252            else_branch,
253        } => {
254            if let Some(base) = base {
255                **base = rewrite_set_calls(
256                    engine,
257                    resolver,
258                    (**base).clone(),
259                    calls,
260                    call_relation,
261                    schema,
262                    params,
263                )?;
264            }
265            for (condition, result) in when {
266                *condition = rewrite_set_calls(
267                    engine,
268                    resolver,
269                    condition.clone(),
270                    calls,
271                    call_relation,
272                    schema,
273                    params,
274                )?;
275                *result = rewrite_set_calls(
276                    engine,
277                    resolver,
278                    result.clone(),
279                    calls,
280                    call_relation,
281                    schema,
282                    params,
283                )?;
284            }
285            if let Some(branch) = else_branch {
286                **branch = rewrite_set_calls(
287                    engine,
288                    resolver,
289                    (**branch).clone(),
290                    calls,
291                    call_relation,
292                    schema,
293                    params,
294                )?;
295            }
296        }
297        ScalarExpr::InSubquery { expr, .. } => {
298            **expr = rewrite_set_calls(
299                engine,
300                resolver,
301                (**expr).clone(),
302                calls,
303                call_relation,
304                schema,
305                params,
306            )?;
307        }
308        ScalarExpr::Default
309        | ScalarExpr::Star
310        | ScalarExpr::QualifiedStar(_)
311        | ScalarExpr::Column(_)
312        | ScalarExpr::Position(_)
313        | ScalarExpr::InternalColumn(_)
314        | ScalarExpr::QualifiedColumn { .. }
315        | ScalarExpr::Literal(_)
316        | ScalarExpr::TypedLiteral { .. }
317        | ScalarExpr::Param(_)
318        | ScalarExpr::ScalarSubquery(_)
319        | ScalarExpr::Exists { .. } => {}
320    }
321    if let ScalarExpr::Func {
322        name,
323        binding,
324        args,
325        ..
326    } = &expression
327    {
328        if function_may_return_set(
329            engine,
330            resolver,
331            name,
332            binding.as_ref(),
333            args,
334            schema,
335            params,
336        )? {
337            let binding = resolve_set_function_binding(
338                engine,
339                resolver,
340                name,
341                binding.as_ref(),
342                args,
343                schema,
344                params,
345            )?
346            .or_else(|| binding.clone());
347            let level = calls[descendant_start..]
348                .iter()
349                .map(|call| call.level + 1)
350                .max()
351                .unwrap_or(0);
352            let placeholder = call_relation.column(calls.len());
353            calls.push(SetFunctionCall {
354                placeholder,
355                name: name.clone(),
356                binding,
357                args: args.clone(),
358                level,
359            });
360            return Ok(ScalarExpr::InternalColumn(placeholder));
361        }
362    }
363    Ok(expression)
364}
365
366fn rewrite_set_frame_bound(
367    engine: &dyn SetFunctionCatalog,
368    resolver: &dyn FunctionTypeResolver,
369    bound: &mut ScalarFrameBound,
370    calls: &mut Vec<SetFunctionCall>,
371    call_relation: crate::ast::InternalRelationId,
372    schema: &RowSchema,
373    params: &[SQLParam],
374) -> Result<(), SQLError> {
375    match bound {
376        ScalarFrameBound::Preceding(expression) | ScalarFrameBound::Following(expression) => {
377            **expression = rewrite_set_calls(
378                engine,
379                resolver,
380                (**expression).clone(),
381                calls,
382                call_relation,
383                schema,
384                params,
385            )?;
386        }
387        ScalarFrameBound::UnboundedPreceding
388        | ScalarFrameBound::UnboundedFollowing
389        | ScalarFrameBound::CurrentRow => {}
390    }
391    Ok(())
392}
393
394fn replace_group_set_expression(
395    expression: &mut ScalarExpr,
396    mappings: &[(ScalarExpr, crate::ast::InternalColumnRef)],
397) {
398    if let Some((_, column)) = mappings
399        .iter()
400        .find(|(group, _)| exprs_match(expression, group))
401    {
402        *expression = ScalarExpr::InternalColumn(*column);
403        return;
404    }
405    match expression {
406        ScalarExpr::Func {
407            args,
408            order_by,
409            filter,
410            ..
411        } => {
412            for expression in args
413                .iter_mut()
414                .chain(order_by.iter_mut().map(|order| &mut order.expr))
415                .chain(filter.as_deref_mut())
416            {
417                replace_group_set_expression(expression, mappings);
418            }
419        }
420        ScalarExpr::Array(items)
421        | ScalarExpr::Row(items)
422        | ScalarExpr::CompositeRow { items, .. }
423        | ScalarExpr::And(items)
424        | ScalarExpr::Or(items) => {
425            for item in items {
426                replace_group_set_expression(item, mappings);
427            }
428        }
429        ScalarExpr::Binary { lhs, rhs, .. } => {
430            replace_group_set_expression(lhs, mappings);
431            replace_group_set_expression(rhs, mappings);
432        }
433        ScalarExpr::Not(inner)
434        | ScalarExpr::UnaryMinus(inner)
435        | ScalarExpr::IsNull { expr: inner, .. }
436        | ScalarExpr::Cast { expr: inner, .. } => {
437            replace_group_set_expression(inner, mappings);
438        }
439        ScalarExpr::Between { expr, low, high } => {
440            replace_group_set_expression(expr, mappings);
441            replace_group_set_expression(low, mappings);
442            replace_group_set_expression(high, mappings);
443        }
444        ScalarExpr::InList { expr, list, .. } => {
445            replace_group_set_expression(expr, mappings);
446            for item in list {
447                replace_group_set_expression(item, mappings);
448            }
449        }
450        ScalarExpr::WindowCall {
451            args, spec, filter, ..
452        } => {
453            for expression in args
454                .iter_mut()
455                .chain(filter.as_deref_mut())
456                .chain(&mut spec.partition_by)
457                .chain(spec.order_by.iter_mut().map(|order| &mut order.expr))
458            {
459                replace_group_set_expression(expression, mappings);
460            }
461            if let Some(frame) = &mut spec.frame {
462                replace_group_set_frame_bound(&mut frame.start, mappings);
463                replace_group_set_frame_bound(&mut frame.end, mappings);
464            }
465        }
466        ScalarExpr::Case {
467            base,
468            when,
469            else_branch,
470        } => {
471            if let Some(base) = base {
472                replace_group_set_expression(base, mappings);
473            }
474            for (condition, result) in when {
475                replace_group_set_expression(condition, mappings);
476                replace_group_set_expression(result, mappings);
477            }
478            if let Some(branch) = else_branch {
479                replace_group_set_expression(branch, mappings);
480            }
481        }
482        ScalarExpr::InSubquery { expr, .. } => {
483            replace_group_set_expression(expr, mappings);
484        }
485        ScalarExpr::Default
486        | ScalarExpr::Star
487        | ScalarExpr::QualifiedStar(_)
488        | ScalarExpr::Column(_)
489        | ScalarExpr::Position(_)
490        | ScalarExpr::InternalColumn(_)
491        | ScalarExpr::QualifiedColumn { .. }
492        | ScalarExpr::Literal(_)
493        | ScalarExpr::TypedLiteral { .. }
494        | ScalarExpr::Param(_)
495        | ScalarExpr::ScalarSubquery(_)
496        | ScalarExpr::Exists { .. } => {}
497    }
498}
499
500fn replace_group_set_frame_bound(
501    bound: &mut ScalarFrameBound,
502    mappings: &[(ScalarExpr, crate::ast::InternalColumnRef)],
503) {
504    match bound {
505        ScalarFrameBound::Preceding(expression) | ScalarFrameBound::Following(expression) => {
506            replace_group_set_expression(expression, mappings);
507        }
508        ScalarFrameBound::UnboundedPreceding
509        | ScalarFrameBound::UnboundedFollowing
510        | ScalarFrameBound::CurrentRow => {}
511    }
512}
513
514pub fn prepare_group_set_projection(
515    engine: &dyn SetFunctionCatalog,
516    resolver: &dyn FunctionTypeResolver,
517    statement: &QueryBlockPlan,
518    schema: &RowSchema,
519    params: &[SQLParam],
520) -> Result<Option<GroupSetProjectionPlan>, SQLError> {
521    let mut groups = Vec::new();
522    for expression in statement
523        .group_by
524        .iter()
525        .chain(statement.grouping_sets.iter().flatten())
526    {
527        if expression_may_return_set(engine, resolver, expression, schema, params)?
528            && !groups
529                .iter()
530                .any(|existing| exprs_match(existing, expression))
531        {
532            groups.push(expression.clone());
533        }
534    }
535    if groups.is_empty() {
536        return Ok(None);
537    }
538
539    let relation = crate::ast::InternalRelationId::allocate();
540    let mappings = groups
541        .iter()
542        .enumerate()
543        .map(|(index, expression)| (expression.clone(), relation.column(index)))
544        .collect::<Vec<_>>();
545    let projections = mappings
546        .iter()
547        .map(|(expression, column)| (ProjectionTarget::Internal(*column), expression.clone()))
548        .collect();
549    let mut rewritten = statement.clone();
550    let projection_labels = projection_columns(&rewritten.projections);
551    for expression in &mut rewritten.group_by {
552        replace_group_set_expression(expression, &mappings);
553    }
554    for set in &mut rewritten.grouping_sets {
555        for expression in set {
556            replace_group_set_expression(expression, &mappings);
557        }
558    }
559    for (projection, label) in rewritten.projections.iter_mut().zip(projection_labels) {
560        if projection.alias.is_none() {
561            projection.alias = Some(label);
562        }
563        replace_group_set_expression(&mut projection.expr, &mappings);
564    }
565    if let Some(having) = &mut rewritten.having {
566        replace_group_set_expression(having, &mappings);
567    }
568    for order in &mut rewritten.order_by {
569        replace_group_set_expression(&mut order.expr, &mappings);
570    }
571    for expression in &mut rewritten.distinct_on {
572        replace_group_set_expression(expression, &mappings);
573    }
574    Ok(Some(GroupSetProjectionPlan {
575        statement: rewritten,
576        projections,
577    }))
578}
579
580fn capture_aggregate_dependency(
581    expression: &ScalarExpr,
582    dependencies: &mut Vec<ProjectionPlan>,
583) -> ScalarExpr {
584    let position = dependencies.len();
585    dependencies.push(ProjectionPlan {
586        expr: expression.clone(),
587        alias: None,
588    });
589    ScalarExpr::Position(position)
590}
591
592#[expect(
593    clippy::too_many_lines,
594    reason = "preserves SELECT schema and row identity"
595)]
596fn rewrite_aggregate_dependencies(
597    engine: &dyn SetFunctionCatalog,
598    group_by: &[&ScalarExpr],
599    expression: &ScalarExpr,
600    dependencies: &mut Vec<ProjectionPlan>,
601) -> ScalarExpr {
602    if is_aggregate(engine, expression)
603        || group_by.iter().any(|group| exprs_match(expression, group))
604    {
605        return capture_aggregate_dependency(expression, dependencies);
606    }
607    match expression {
608        ScalarExpr::Column(_)
609        | ScalarExpr::Position(_)
610        | ScalarExpr::InternalColumn(_)
611        | ScalarExpr::QualifiedColumn { .. } => {
612            capture_aggregate_dependency(expression, dependencies)
613        }
614        ScalarExpr::Func {
615            order_syntax,
616            name,
617            binding,
618            args,
619            distinct,
620            order_by,
621            filter,
622        } => ScalarExpr::Func {
623            order_syntax: *order_syntax,
624            name: name.clone(),
625            binding: binding.clone(),
626            args: args
627                .iter()
628                .map(|argument| {
629                    rewrite_aggregate_dependencies(engine, group_by, argument, dependencies)
630                })
631                .collect(),
632            distinct: *distinct,
633            order_by: order_by
634                .iter()
635                .map(|order| {
636                    let mut order = order.clone();
637                    order.expr =
638                        rewrite_aggregate_dependencies(engine, group_by, &order.expr, dependencies);
639                    order
640                })
641                .collect(),
642            filter: filter.as_deref().map(|filter| {
643                Box::new(rewrite_aggregate_dependencies(
644                    engine,
645                    group_by,
646                    filter,
647                    dependencies,
648                ))
649            }),
650        },
651        ScalarExpr::Array(items) => ScalarExpr::Array(
652            items
653                .iter()
654                .map(|item| rewrite_aggregate_dependencies(engine, group_by, item, dependencies))
655                .collect(),
656        ),
657        ScalarExpr::Row(items) => ScalarExpr::Row(
658            items
659                .iter()
660                .map(|item| rewrite_aggregate_dependencies(engine, group_by, item, dependencies))
661                .collect(),
662        ),
663        ScalarExpr::CompositeRow {
664            items,
665            binding,
666            bound_type,
667        } => ScalarExpr::CompositeRow {
668            items: items
669                .iter()
670                .map(|item| rewrite_aggregate_dependencies(engine, group_by, item, dependencies))
671                .collect(),
672            binding: binding.clone(),
673            bound_type: bound_type.clone(),
674        },
675        ScalarExpr::Binary { op, lhs, rhs } => ScalarExpr::Binary {
676            op: *op,
677            lhs: Box::new(rewrite_aggregate_dependencies(
678                engine,
679                group_by,
680                lhs,
681                dependencies,
682            )),
683            rhs: Box::new(rewrite_aggregate_dependencies(
684                engine,
685                group_by,
686                rhs,
687                dependencies,
688            )),
689        },
690        ScalarExpr::Not(inner) => ScalarExpr::Not(Box::new(rewrite_aggregate_dependencies(
691            engine,
692            group_by,
693            inner,
694            dependencies,
695        ))),
696        ScalarExpr::UnaryMinus(inner) => ScalarExpr::UnaryMinus(Box::new(
697            rewrite_aggregate_dependencies(engine, group_by, inner, dependencies),
698        )),
699        ScalarExpr::And(items) => ScalarExpr::And(
700            items
701                .iter()
702                .map(|item| rewrite_aggregate_dependencies(engine, group_by, item, dependencies))
703                .collect(),
704        ),
705        ScalarExpr::Or(items) => ScalarExpr::Or(
706            items
707                .iter()
708                .map(|item| rewrite_aggregate_dependencies(engine, group_by, item, dependencies))
709                .collect(),
710        ),
711        ScalarExpr::IsNull { expr, negated } => ScalarExpr::IsNull {
712            expr: Box::new(rewrite_aggregate_dependencies(
713                engine,
714                group_by,
715                expr,
716                dependencies,
717            )),
718            negated: *negated,
719        },
720        ScalarExpr::Between { expr, low, high } => ScalarExpr::Between {
721            expr: Box::new(rewrite_aggregate_dependencies(
722                engine,
723                group_by,
724                expr,
725                dependencies,
726            )),
727            low: Box::new(rewrite_aggregate_dependencies(
728                engine,
729                group_by,
730                low,
731                dependencies,
732            )),
733            high: Box::new(rewrite_aggregate_dependencies(
734                engine,
735                group_by,
736                high,
737                dependencies,
738            )),
739        },
740        ScalarExpr::InList {
741            expr,
742            list,
743            negated,
744        } => ScalarExpr::InList {
745            expr: Box::new(rewrite_aggregate_dependencies(
746                engine,
747                group_by,
748                expr,
749                dependencies,
750            )),
751            list: list
752                .iter()
753                .map(|item| rewrite_aggregate_dependencies(engine, group_by, item, dependencies))
754                .collect(),
755            negated: *negated,
756        },
757        ScalarExpr::WindowCall {
758            name,
759            args,
760            spec,
761            filter,
762            modifiers,
763        } => {
764            let mut spec = spec.clone();
765            spec.partition_by = spec
766                .partition_by
767                .iter()
768                .map(|item| rewrite_aggregate_dependencies(engine, group_by, item, dependencies))
769                .collect();
770            for order in &mut spec.order_by {
771                order.expr =
772                    rewrite_aggregate_dependencies(engine, group_by, &order.expr, dependencies);
773            }
774            if let Some(frame) = &mut spec.frame {
775                rewrite_aggregate_frame_bound(engine, group_by, &mut frame.start, dependencies);
776                rewrite_aggregate_frame_bound(engine, group_by, &mut frame.end, dependencies);
777            }
778            ScalarExpr::WindowCall {
779                modifiers: *modifiers,
780                name: name.clone(),
781                args: args
782                    .iter()
783                    .map(|argument| {
784                        rewrite_aggregate_dependencies(engine, group_by, argument, dependencies)
785                    })
786                    .collect(),
787                spec,
788                filter: filter.as_deref().map(|filter| {
789                    Box::new(rewrite_aggregate_dependencies(
790                        engine,
791                        group_by,
792                        filter,
793                        dependencies,
794                    ))
795                }),
796            }
797        }
798        ScalarExpr::Case {
799            base,
800            when,
801            else_branch,
802        } => ScalarExpr::Case {
803            base: base.as_deref().map(|base| {
804                Box::new(rewrite_aggregate_dependencies(
805                    engine,
806                    group_by,
807                    base,
808                    dependencies,
809                ))
810            }),
811            when: when
812                .iter()
813                .map(|(condition, result)| {
814                    (
815                        rewrite_aggregate_dependencies(engine, group_by, condition, dependencies),
816                        rewrite_aggregate_dependencies(engine, group_by, result, dependencies),
817                    )
818                })
819                .collect(),
820            else_branch: else_branch.as_deref().map(|branch| {
821                Box::new(rewrite_aggregate_dependencies(
822                    engine,
823                    group_by,
824                    branch,
825                    dependencies,
826                ))
827            }),
828        },
829        ScalarExpr::Cast { expr, ty, implicit } => ScalarExpr::Cast {
830            implicit: *implicit,
831            expr: Box::new(rewrite_aggregate_dependencies(
832                engine,
833                group_by,
834                expr,
835                dependencies,
836            )),
837            ty: ty.clone(),
838        },
839        ScalarExpr::InSubquery {
840            expr,
841            subquery,
842            negated,
843        } => ScalarExpr::InSubquery {
844            expr: Box::new(rewrite_aggregate_dependencies(
845                engine,
846                group_by,
847                expr,
848                dependencies,
849            )),
850            subquery: *subquery,
851            negated: *negated,
852        },
853        ScalarExpr::Default
854        | ScalarExpr::Star
855        | ScalarExpr::QualifiedStar(_)
856        | ScalarExpr::Literal(_)
857        | ScalarExpr::TypedLiteral { .. }
858        | ScalarExpr::Param(_)
859        | ScalarExpr::ScalarSubquery(_)
860        | ScalarExpr::Exists { .. } => expression.clone(),
861    }
862}
863
864fn rewrite_aggregate_frame_bound(
865    engine: &dyn SetFunctionCatalog,
866    group_by: &[&ScalarExpr],
867    bound: &mut ScalarFrameBound,
868    dependencies: &mut Vec<ProjectionPlan>,
869) {
870    match bound {
871        ScalarFrameBound::Preceding(expression) | ScalarFrameBound::Following(expression) => {
872            **expression =
873                rewrite_aggregate_dependencies(engine, group_by, expression, dependencies);
874        }
875        ScalarFrameBound::UnboundedPreceding
876        | ScalarFrameBound::UnboundedFollowing
877        | ScalarFrameBound::CurrentRow => {}
878    }
879}
880
881pub fn prepare_aggregate_output_projection(
882    engine: &dyn SetFunctionCatalog,
883    statement: &QueryBlockPlan,
884    internal_targets: &[(usize, crate::ast::InternalColumnRef)],
885) -> AggregateOutputProjectionPlan {
886    let labels = projection_columns(&statement.projections);
887    // A grouping-set expression remains one aggregate dependency. Splitting it into its input columns would replace those ungrouped inputs with NULL before evaluating the final projection.
888    let group_by = statement
889        .group_by
890        .iter()
891        .chain(statement.grouping_sets.iter().flatten())
892        .collect::<Vec<_>>();
893    let mut dependencies = Vec::new();
894    let projections = statement
895        .projections
896        .iter()
897        .enumerate()
898        .zip(labels)
899        .map(|((position, projection), label)| {
900            let target = internal_targets
901                .iter()
902                .find(|(target_position, _)| *target_position == position)
903                .map_or_else(
904                    || ProjectionTarget::Column(label),
905                    |(_, column)| ProjectionTarget::Internal(*column),
906                );
907            (
908                target,
909                rewrite_aggregate_dependencies(
910                    engine,
911                    &group_by,
912                    &projection.expr,
913                    &mut dependencies,
914                ),
915            )
916        })
917        .collect();
918    if dependencies.is_empty() {
919        dependencies.push(ProjectionPlan {
920            expr: ScalarExpr::Literal(Value::Int(1)),
921            alias: None,
922        });
923    }
924    let mut aggregate_statement = statement.clone();
925    aggregate_statement.projections = dependencies;
926    AggregateOutputProjectionPlan {
927        statement: aggregate_statement,
928        projections,
929    }
930}