Walks an expression tree top-down, extracting `MoveTowardsLeafNodes` sub-expressions and routing each to the correct per-input extractor.
(
expr: Expr,
extractors: &mut [LeafExpressionExtractor],
input_column_sets: &[std::collections::HashSet<ColumnReference>],
)
| 289 | /// Walks an expression tree top-down, extracting `MoveTowardsLeafNodes` |
| 290 | /// sub-expressions and routing each to the correct per-input extractor. |
| 291 | fn routing_extract( |
| 292 | expr: Expr, |
| 293 | extractors: &mut [LeafExpressionExtractor], |
| 294 | input_column_sets: &[std::collections::HashSet<ColumnReference>], |
| 295 | ) -> Result<Transformed<Expr>> { |
| 296 | expr.transform_down(|e| { |
| 297 | // Skip expressions already aliased with extracted expression pattern |
| 298 | if let Expr::Alias(alias) = &e |
| 299 | && alias.name.starts_with(EXTRACTED_EXPR_PREFIX) |
| 300 | { |
| 301 | return Ok(Transformed { |
| 302 | data: e, |
| 303 | transformed: false, |
| 304 | tnr: TreeNodeRecursion::Jump, |
| 305 | }); |
| 306 | } |
| 307 | |
| 308 | // Don't extract Alias nodes directly — preserve the alias and let |
| 309 | // transform_down recurse into the inner expression |
| 310 | if matches!(&e, Expr::Alias(_)) { |
| 311 | return Ok(Transformed::no(e)); |
| 312 | } |
| 313 | |
| 314 | match e.placement() { |
| 315 | ExpressionPlacement::MoveTowardsLeafNodes => { |
| 316 | if let Some(idx) = find_owning_input(&e, input_column_sets) { |
| 317 | let col_ref = extractors[idx].add_extracted(e)?; |
| 318 | Ok(Transformed::yes(col_ref)) |
| 319 | } else { |
| 320 | // References columns from multiple inputs — cannot extract |
| 321 | Ok(Transformed::no(e)) |
| 322 | } |
| 323 | } |
| 324 | ExpressionPlacement::Column => { |
| 325 | // Track columns that the parent node references so the |
| 326 | // extraction projection includes them as pass-through. |
| 327 | // Without this, the extraction projection would only |
| 328 | // contain __datafusion_extracted_N aliases, and the parent couldn't |
| 329 | // resolve its other column references. |
| 330 | if let Expr::Column(col) = &e |
| 331 | && let Some(idx) = find_owning_input(&e, input_column_sets) |
| 332 | { |
| 333 | extractors[idx].columns_needed.insert(col.clone()); |
| 334 | } |
| 335 | Ok(Transformed::no(e)) |
| 336 | } |
| 337 | _ => Ok(Transformed::no(e)), |
| 338 | } |
| 339 | }) |
| 340 | } |
| 341 | |
| 342 | /// Rewrites extraction pairs and column references from one qualifier |
| 343 | /// space to another. |
no test coverage detected
searching dependent graphs…