The rewriting only happens when the traversal has reached the top-most unnest expr within a sequence of consecutive unnest exprs node For example an expr of **unnest(unnest(column1)) + unnest(unnest(unnest(column2))) ```text ┌──────────────────┐ │ binaryexpr │ │ │ └──────────────────┘ f_down / / │ │ / / f_up │ │ / / f_down│ │f_up unnest
(&mut self, expr: Expr)
| 560 | /// column2 |
| 561 | /// ``` |
| 562 | fn f_up(&mut self, expr: Expr) -> Result<Transformed<Expr>> { |
| 563 | if let Expr::Unnest(ref traversing_unnest) = expr { |
| 564 | if traversing_unnest == self.top_most_unnest.as_ref().unwrap() { |
| 565 | self.top_most_unnest = None; |
| 566 | } |
| 567 | // Find inside consecutive_unnest, the sequence of continuous unnest exprs |
| 568 | |
| 569 | // Get the latest consecutive unnest exprs |
| 570 | // and check if current upward traversal is the returning to the root expr |
| 571 | // for example given a expr `unnest(unnest(col))` then the traversal happens like: |
| 572 | // down(unnest) -> down(unnest) -> down(col) -> up(col) -> up(unnest) -> up(unnest) |
| 573 | // the result of such traversal is unnest(col, depth:=2) |
| 574 | let unnest_stack = self.get_latest_consecutive_unnest(); |
| 575 | |
| 576 | // This traversal has reached the top most unnest again |
| 577 | // e.g Unnest(top) -> Unnest(2nd) -> Column(bottom) |
| 578 | // -> Unnest(2nd) -> Unnest(top) a.k.a here |
| 579 | // Thus |
| 580 | // Unnest(Unnest(some_col)) is rewritten into Unnest(some_col, depth:=2) |
| 581 | if traversing_unnest == unnest_stack.last().unwrap() { |
| 582 | let most_inner = unnest_stack.first().unwrap(); |
| 583 | let inner_expr = most_inner.expr.as_ref(); |
| 584 | // unnest(unnest(struct_arr_col)) is not allow to be done recursively |
| 585 | // it needs to be split into multiple unnest logical plan |
| 586 | // unnest(struct_arr) |
| 587 | // unnest(struct_arr_col) as struct_arr |
| 588 | // instead of unnest(struct_arr_col, depth = 2) |
| 589 | |
| 590 | let unnest_recursion = unnest_stack.len(); |
| 591 | let struct_allowed = |
| 592 | self.is_at_struct_allowed_root(&expr) && unnest_recursion == 1; |
| 593 | |
| 594 | let mut transformed_exprs = self.transform( |
| 595 | unnest_recursion, |
| 596 | expr.schema_name().to_string(), |
| 597 | inner_expr, |
| 598 | struct_allowed, |
| 599 | )?; |
| 600 | // Only set transformed_root_exprs for struct unnest (which returns multiple expressions). |
| 601 | // For list unnest (single expression), we let the normal rewrite handle the alias. |
| 602 | if struct_allowed && transformed_exprs.len() > 1 { |
| 603 | self.transformed_root_exprs = Some(transformed_exprs.clone()); |
| 604 | } |
| 605 | return Ok(Transformed::new( |
| 606 | transformed_exprs.swap_remove(0), |
| 607 | true, |
| 608 | TreeNodeRecursion::Continue, |
| 609 | )); |
| 610 | } |
| 611 | } else { |
| 612 | self.consecutive_unnest.push(None); |
| 613 | } |
| 614 | |
| 615 | // For column exprs that are not descendants of any unnest node |
| 616 | // retain their projection |
| 617 | // e.g given expr tree unnest(col_a) + col_b, we have to retain projection of col_b |
| 618 | // this condition can be checked by maintaining an Option<top most unnest> |
| 619 | if matches!(&expr, Expr::Column(_)) && self.top_most_unnest.is_none() { |
nothing calls this directly
no test coverage detected