@@ -228,6 +228,7 @@ fn optimize_projections(
228228 . transform_data ( |plan| optimize_subqueries ( plan, config) ) ;
229229 }
230230 LogicalPlan :: Window ( window) => {
231+ let original_window = window. clone ( ) ;
231232 let input_schema = Arc :: clone ( window. input . schema ( ) ) ;
232233 // Split parent requirements to child and window expression sections:
233234 let n_input_fields = input_schema. fields ( ) . len ( ) ;
@@ -238,37 +239,46 @@ fn optimize_projections(
238239 // Only use window expressions that are absolutely necessary according
239240 // to parent requirements:
240241 let new_window_expr = window_reqs. get_at_indices ( & window. window_expr ) ;
242+ let window_expr_changed = new_window_expr != window. window_expr ;
241243
242244 // Get all the required column indices at the input, either by the
243245 // parent or window expression requirements.
244246 let required_indices = child_reqs. with_exprs ( & input_schema, & new_window_expr) ;
245247
246- return optimize_projections (
248+ let transformed_input = optimize_projections (
247249 Arc :: unwrap_or_clone ( window. input ) ,
248250 config,
249251 required_indices. clone ( ) ,
250- ) ?
251- . transform_data ( |window_child| {
252- if new_window_expr. is_empty ( ) {
253- // When no window expression is necessary, use the input directly:
254- Ok ( Transformed :: no ( window_child) )
255- } else {
256- // Calculate required expressions at the input of the window.
257- // Please note that we use `input_schema`, because `required_indices`
258- // refers to that schema
259- let required_exprs =
260- required_indices. get_required_exprs ( & input_schema) ;
261- let window_child =
262- add_projection_on_top_if_helpful ( window_child, required_exprs) ?
263- . data ;
264- Window :: try_new ( new_window_expr, Arc :: new ( window_child) )
252+ ) ?;
253+ let input_changed = transformed_input. transformed ;
254+ let window_child = transformed_input. data ;
255+
256+ let transformed_plan = if new_window_expr. is_empty ( ) {
257+ // When no window expression is necessary, use the input directly:
258+ Transformed :: yes ( window_child)
259+ } else {
260+ // Calculate required expressions at the input of the window.
261+ // Please note that we use `input_schema`, because `required_indices`
262+ // refers to that schema
263+ let required_exprs = required_indices. get_required_exprs ( & input_schema) ;
264+ let projected_input =
265+ add_projection_on_top_if_helpful ( window_child, required_exprs) ?;
266+ let input_changed = input_changed || projected_input. transformed ;
267+
268+ if window_expr_changed || input_changed {
269+ Window :: try_new ( new_window_expr, Arc :: new ( projected_input. data ) )
265270 . map ( LogicalPlan :: Window )
266- . map ( Transformed :: yes)
271+ . map ( Transformed :: yes) ?
272+ } else {
273+ Transformed :: no ( LogicalPlan :: Window ( original_window) )
267274 }
268- } ) ?
269- . transform_data ( |plan| optimize_subqueries ( plan, config) ) ;
275+ } ;
276+
277+ return transformed_plan
278+ . transform_data ( |plan| optimize_subqueries ( plan, config) ) ;
270279 }
271280 LogicalPlan :: TableScan ( table_scan) => {
281+ let original_scan = table_scan. clone ( ) ;
272282 let TableScan {
273283 table_name,
274284 source,
@@ -286,6 +296,9 @@ fn optimize_projections(
286296 } ;
287297 let new_scan =
288298 TableScan :: try_new ( table_name, source, Some ( projection) , filters, fetch) ?;
299+ if new_scan == original_scan {
300+ return Ok ( Transformed :: no ( LogicalPlan :: TableScan ( original_scan) ) ) ;
301+ }
289302
290303 return Transformed :: yes ( LogicalPlan :: TableScan ( new_scan) )
291304 . transform_data ( |plan| optimize_subqueries ( plan, config) ) ;
@@ -856,6 +869,7 @@ fn rewrite_projection_given_requirements(
856869 config : & dyn OptimizerConfig ,
857870 indices : & RequiredIndices ,
858871) -> Result < Transformed < LogicalPlan > > {
872+ let original_plan = LogicalPlan :: Projection ( proj. clone ( ) ) ;
859873 let Projection { expr, input, .. } = proj;
860874
861875 let exprs_used = indices. get_at_indices ( & expr) ;
@@ -865,16 +879,24 @@ fn rewrite_projection_given_requirements(
865879
866880 // rewrite the children projection, and if they are changed rewrite the
867881 // projection down
868- optimize_projections ( Arc :: unwrap_or_clone ( input) , config, required_indices) ?
869- . transform_data ( |input| {
870- if is_projection_unnecessary ( & input, & exprs_used) ? {
871- Ok ( Transformed :: yes ( input) )
872- } else {
873- Projection :: try_new ( exprs_used, Arc :: new ( input) )
874- . map ( LogicalPlan :: Projection )
875- . map ( Transformed :: yes)
876- }
877- } )
882+ let transformed_input = optimize_projections (
883+ Arc :: unwrap_or_clone ( Arc :: clone ( & input) ) ,
884+ config,
885+ required_indices,
886+ ) ?;
887+ let new_input = transformed_input. data ;
888+
889+ if is_projection_unnecessary ( & new_input, & exprs_used) ? {
890+ return Ok ( Transformed :: yes ( new_input) ) ;
891+ }
892+
893+ let new_plan = Projection :: try_new ( exprs_used, Arc :: new ( new_input) )
894+ . map ( LogicalPlan :: Projection ) ?;
895+ if new_plan == original_plan {
896+ Ok ( Transformed :: no ( original_plan) )
897+ } else {
898+ Ok ( Transformed :: yes ( new_plan) )
899+ }
878900}
879901
880902/// Projection is unnecessary, when
0 commit comments