1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use crate::engine::{
exec::{If, VarStore},
mir,
};
/// The executable form of a query: the MIR operation graph plus the step
/// sequence that runs it. Steps reference operations by [`mir::NodeId`],
/// which doubles as the runtime variable slot for the operation's output.
#[derive(Debug)]
pub(crate) struct ExecPlan {
/// The operation graph the steps execute.
pub(crate) plan: mir::LogicalPlan,
/// Runtime storage for node outputs.
pub(crate) vars: VarStore,
/// Steps in the pipeline.
pub(crate) steps: Vec<Step>,
/// The node whose output the pipeline returns.
pub(crate) returning: mir::NodeId,
/// When true, the executor wraps the entire plan in a transaction.
pub(crate) needs_transaction: bool,
}
/// A single step of the exec program.
#[derive(Debug)]
pub(crate) enum Step {
/// Execute one node's operation and store its output.
Run(mir::NodeId),
/// Conditionally execute a block of pure nodes.
If(If),
}
impl Step {
/// Returns the step's name for logging.
pub(crate) fn name(&self, plan: &mir::LogicalPlan) -> &'static str {
match self {
Step::Run(node_id) => plan[node_id].op.name(),
Step::If(_) => "if",
}
}
/// Returns the number of database operations this step issues, counting
/// into `If` arms. The count is static: a skipped `If` arm can leave a
/// transaction wrapping a single executed operation, which is harmless.
pub(crate) fn db_op_count(&self, plan: &mir::LogicalPlan) -> usize {
match self {
Step::Run(node_id) => plan[node_id].op.is_db_op() as usize,
Step::If(action) => action
.then
.iter()
.filter(|node_id| plan[*node_id].op.is_db_op())
.count(),
}
}
}