Coverage Report

Created: 2024-10-13 08:39

/Users/andrewlamb/Software/datafusion/datafusion/physical-plan/src/visitor.rs
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
use super::ExecutionPlan;
19
20
/// Visit all children of this plan, according to the order defined on `ExecutionPlanVisitor`.
21
// Note that this would be really nice if it were a method on
22
// ExecutionPlan, but it can not be because it takes a generic
23
// parameter and `ExecutionPlan` is a trait
24
5
pub fn accept<V: ExecutionPlanVisitor>(
25
5
    plan: &dyn ExecutionPlan,
26
5
    visitor: &mut V,
27
5
) -> Result<(), V::Error> {
28
5
    visitor.pre_visit(plan)
?0
;
29
5
    for child in plan.children() {
30
5
        visit_execution_plan(child.as_ref(), visitor)
?0
;
31
    }
32
5
    visitor.post_visit(plan)
?0
;
33
5
    Ok(())
34
5
}
35
36
/// Trait that implements the [Visitor
37
/// pattern](https://en.wikipedia.org/wiki/Visitor_pattern) for a
38
/// depth first walk of `ExecutionPlan` nodes. `pre_visit` is called
39
/// before any children are visited, and then `post_visit` is called
40
/// after all children have been visited.
41
///
42
/// To use, define a struct that implements this trait and then invoke
43
/// ['accept'].
44
///
45
/// For example, for an execution plan that looks like:
46
///
47
/// ```text
48
/// ProjectionExec: id
49
///    FilterExec: state = CO
50
///       CsvExec:
51
/// ```
52
///
53
/// The sequence of visit operations would be:
54
/// ```text
55
/// visitor.pre_visit(ProjectionExec)
56
/// visitor.pre_visit(FilterExec)
57
/// visitor.pre_visit(CsvExec)
58
/// visitor.post_visit(CsvExec)
59
/// visitor.post_visit(FilterExec)
60
/// visitor.post_visit(ProjectionExec)
61
/// ```
62
pub trait ExecutionPlanVisitor {
63
    /// The type of error returned by this visitor
64
    type Error;
65
66
    /// Invoked on an `ExecutionPlan` plan before any of its child
67
    /// inputs have been visited. If Ok(true) is returned, the
68
    /// recursion continues. If Err(..) or Ok(false) are returned, the
69
    /// recursion stops immediately and the error, if any, is returned
70
    /// to `accept`
71
    fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> Result<bool, Self::Error>;
72
73
    /// Invoked on an `ExecutionPlan` plan *after* all of its child
74
    /// inputs have been visited. The return value is handled the same
75
    /// as the return value of `pre_visit`. The provided default
76
    /// implementation returns `Ok(true)`.
77
0
    fn post_visit(&mut self, _plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
78
0
        Ok(true)
79
0
    }
80
}
81
82
/// Recursively calls `pre_visit` and `post_visit` for this node and
83
/// all of its children, as described on [`ExecutionPlanVisitor`]
84
10
pub fn visit_execution_plan<V: ExecutionPlanVisitor>(
85
10
    plan: &dyn ExecutionPlan,
86
10
    visitor: &mut V,
87
10
) -> Result<(), V::Error> {
88
10
    visitor.pre_visit(plan)
?0
;
89
10
    for 
child5
in plan.children() {
90
5
        visit_execution_plan(child.as_ref(), visitor)
?0
;
91
    }
92
10
    visitor.post_visit(plan)
?0
;
93
10
    Ok(())
94
10
}