(
&self,
window: Window,
config: &dyn OptimizerConfig,
)
| 144 | } |
| 145 | |
| 146 | fn try_optimize_window( |
| 147 | &self, |
| 148 | window: Window, |
| 149 | config: &dyn OptimizerConfig, |
| 150 | ) -> Result<Transformed<LogicalPlan>> { |
| 151 | // Collects window expressions from consecutive `LogicalPlan::Window` nodes into |
| 152 | // a list. |
| 153 | let (window_expr_list, window_schemas, input) = |
| 154 | get_consecutive_window_exprs(window); |
| 155 | |
| 156 | // Extract common sub-expressions from the list. |
| 157 | |
| 158 | match CSE::new(ExprCSEController::new( |
| 159 | config.alias_generator().as_ref(), |
| 160 | ExprMask::Normal, |
| 161 | )) |
| 162 | .extract_common_nodes(window_expr_list)? |
| 163 | { |
| 164 | // If there are common sub-expressions, then the insert a projection node |
| 165 | // with the common expressions between the new window nodes and the |
| 166 | // original input. |
| 167 | FoundCommonNodes::Yes { |
| 168 | common_nodes: common_exprs, |
| 169 | new_nodes_list: new_exprs_list, |
| 170 | original_nodes_list: original_exprs_list, |
| 171 | } => build_common_expr_project_plan(input, common_exprs).map(|new_input| { |
| 172 | Transformed::yes((new_exprs_list, new_input, Some(original_exprs_list))) |
| 173 | }), |
| 174 | FoundCommonNodes::No { |
| 175 | original_nodes_list: original_exprs_list, |
| 176 | } => Ok(Transformed::no((original_exprs_list, input, None))), |
| 177 | }? |
| 178 | // Recurse into the new input. |
| 179 | // (This is similar to what a `ApplyOrder::TopDown` optimizer rule would do.) |
| 180 | .transform_data(|(new_window_expr_list, new_input, window_expr_list)| { |
| 181 | self.rewrite(new_input, config)?.map_data(|new_input| { |
| 182 | Ok((new_window_expr_list, new_input, window_expr_list)) |
| 183 | }) |
| 184 | })? |
| 185 | // Rebuild the consecutive window nodes. |
| 186 | .map_data(|(new_window_expr_list, new_input, window_expr_list)| { |
| 187 | // If there were common expressions extracted, then we need to make sure |
| 188 | // we restore the original column names. |
| 189 | // TODO: Although `find_common_exprs()` inserts aliases around extracted |
| 190 | // common expressions this doesn't mean that the original column names |
| 191 | // (schema) are preserved due to the inserted aliases are not always at |
| 192 | // the top of the expression. |
| 193 | // Let's consider improving `find_common_exprs()` to always keep column |
| 194 | // names and get rid of additional name preserving logic here. |
| 195 | if let Some(window_expr_list) = window_expr_list { |
| 196 | let name_preserver = NamePreserver::new_for_projection(); |
| 197 | let saved_names = window_expr_list |
| 198 | .iter() |
| 199 | .map(|exprs| { |
| 200 | exprs |
| 201 | .iter() |
| 202 | .map(|expr| name_preserver.save(expr)) |
| 203 | .collect::<Vec<_>>() |
no test coverage detected