Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify TreeNode recursions #9965

Merged
merged 1 commit into from
Apr 5, 2024

Conversation

peter-toth
Copy link
Contributor

Which issue does this PR close?

Part of #8913.

Rationale for this change

This PR simplifies TreeNode recursion declarations by adding

  • TreeNodeRecursion::visit_children(),
  • TreeNodeRecursion::visit_sibling() and
  • TreeNodeRecursion::visit_parent()

helpers to simplify handling of the current TreeNodeRecursion with continuation closures by specifying the next node position relative to the current one.

Similarly, the transforming recursions are simplified with

  • Transfomed::visit_children(),
  • Transfomed::visit_sibling() and
  • Transfomed::visit_parent()

helpers.

After this PR recursion definition of TreeNode::visit() is as simple as that:

visitor.f_down(self)?
    .visit_children(|| self.apply_children(|c| c.visit(visitor)))?
    .visit_parent(|| visitor.f_up(self))

and TreeNode::rewrite() became:

rewriter.f_down(self)?
    .transform_children(|n| n.map_children(|c| c.rewrite(rewriter)))?
    .transform_parent(|n| rewriter.f_up(n))

What changes are included in this PR?

This PR:

  • Adds the above helpers and contains some other minor code cleanup.
  • Renames TransformedIterator to TreeNodeIterator and adds a new TreeNodeIterator::apply_until_stop() helper to simplify TreeNode::apply_children() implementations.

Are these changes tested?

Yes, with existint UTs.

Are there any user-facing changes?

No.

@github-actions github-actions bot added logical-expr Logical plan and expressions optimizer Optimizer rules labels Apr 5, 2024
@@ -355,7 +296,7 @@ pub trait TreeNode: Sized {
/// Apply the closure `F` to the node's children.
fn apply_children<F: FnMut(&Self) -> Result<TreeNodeRecursion>>(
&self,
f: &mut F,
f: F,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not related to handling recursions just a small code cleanup.

@peter-toth
Copy link
Contributor Author

cc @alamb, @berkaysynnada

Copy link
Contributor

@alamb alamb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @peter-toth -- I think this looks great to me 🙏

I have one small suggestion on naming but otherwise this looks good to me

TreeNodeRecursion::Jump => visitor.f_up(self),
TreeNodeRecursion::Stop => Ok(TreeNodeRecursion::Stop),
}
visitor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ -- this is really nice. I would have helped when i was fooling around with the TreeNodeMutator

@@ -432,6 +373,45 @@ pub enum TreeNodeRecursion {
Stop,
}

impl TreeNodeRecursion {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are much clearer. 👌 very nice


/// Continues visiting nodes with `f` depending on the current [`TreeNodeRecursion`]
/// value and the fact that `f` is visiting the current node's parent.
pub fn visit_parent<F: FnOnce() -> Result<TreeNodeRecursion>>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was confused at first about the parent term here -- I think it might be clearer to call this function visit_self or visit_node as it is used to visit the node (potentially) after visiting its children, rather than visiting the parent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with visit_self or visit_node. My rationale for the current visit_children / visit_sibling / visit_parent naming was to specify the relative position to the last tnr.

This is a bit more detailed example that I will implement in #9913 for LogicalPlan::visit_with_subqueries and after we fix #9913 (comment) (so the subquery plans are treated like additional children):

// visit the current node (self)
visitor.f_down(self)?

    // `visit_children()` because last tnr is from the current node (self) and the continuation 
    // closure visits the current node (self)'s subqueries (that we treat as children)
    .visit_children(|| self.apply_subqueries(|c| c.visit_with_subqueries(visitor)))? 
    
    // `visit_sibling()` because last tnr is from visiting the last subquery (that we treated
    // as a child) and continuation closure visits the current node (self)'s children, that are
    // siblings to the subqueries
    .visit_sibling(|| self.apply_children(|s| s.visit_with_subqueries(visitor)))?

    // `visit_parent()` because last tnr is from transforming the last child and continuation
    // cloure visits the current node (self, that is the parent of the last child)
    .visit_parent(|| visitor.f_up(self)) 

So I'm ok with renaming visit_parent to visit_self or visit_node, but then visit_sibling sounds weird.

Actually, how about visit_down / visit_next / visit_up?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the current naming is more descriptive than visit_down / visit_next / visit_up -- let's go with what is in this PR for now and we can iterate in the future if needed

/// Maps the [`Transformed`] object to the result of the given `f` depending on the
/// current [`TreeNodeRecursion`] value and the fact that `f` is changing the current
/// node's parent.
pub fn transform_parent<F: FnOnce(T) -> Result<Transformed<T>>>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise here - maybe it would make more sense to call this transform_self or transform_node

@alamb alamb merged commit 27bdf3c into apache:main Apr 5, 2024
24 checks passed
@alamb
Copy link
Contributor

alamb commented Apr 5, 2024

Thanks again @peter-toth

@peter-toth
Copy link
Contributor Author

Thanks for the review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
logical-expr Logical plan and expressions optimizer Optimizer rules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants