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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Attaches guards to plan nodes. A guard is a condition on a node; when the
//! condition is false at execution time, the node is skipped (see `exec::If`).
//!
//! # Why guards exist
//!
//! A plan often contains nodes whose output only matters when an earlier node
//! returned rows. Consider loading todos together with each todo's user:
//!
//! ```text
//! n0: query todos
//! n1: extract the user ids from n0's rows
//! n2: query users whose id is in n1
//! n3: map over n0's rows, attaching each todo's user from n2
//! ```
//!
//! When n0 returns no rows, the map in n3 iterates zero times, so nothing
//! ever reads n2 — the user query is a wasted database round-trip. This pass
//! guards n1 and n2 with the condition "n0 returned at least one row"
//! (`non_empty(n0)`). At execution time, when n0 comes back empty, n1 and n2
//! are skipped.
//!
//! # The two rules
//!
//! A node may be skipped only when nothing reads its output. Two rules prove
//! that:
//!
//! 1. A node read [`InputRead::PerRowOf`]`(X)` — only while iterating node
//! X's rows — gets guard `non_empty(X)`. Iterating zero rows never reads
//! the input, so the node's output goes unread whenever X is empty. This
//! is how n2 gets its guard above: n3 is a per-row `Eval` over n0, and n2
//! is one of its other inputs.
//!
//! 2. A node whose consumers all carry guard `non_empty(X)` inherits that
//! guard. Whenever X is empty, every consumer is skipped, so the node's
//! output goes unread too. This is how n1 gets its guard: its only
//! consumer, n2, is guarded on n0.
//!
//! # When a node gets no guard
//!
//! The analysis is conservative: one consumer that might read the output
//! kills the guard, and the node always executes. In particular:
//!
//! - A node that writes to the database (`Operation::is_effectful`) is never
//! skipped, and never propagates a guard to its inputs.
//! - The completion node's output is the query result; the engine reads it
//! unconditionally.
//! - Consumers guarded on different nodes disagree, so their shared input
//! gets no guard.
//!
//! In the example, n0 stays unguarded: n3 reads it as the row input
//! ([`InputRead::Always`]), and n3 itself has no guard.
//!
//! # How the pass runs
//!
//! The pass keeps one [`Consumption`] value per node. It summarizes how the
//! consumers visited so far read that node's output, and is one of three
//! states: no consumer visited yet ([`Consumption::None`]), every consumer
//! so far reads the output only when node X returned rows
//! ([`Consumption::OnlyUnder`]`(X)`), or some consumer reads the output
//! regardless ([`Consumption::Observed`]).
//!
//! The pass visits nodes in reverse execution order. A node always executes
//! before its consumers, so the reverse walk visits every consumer first,
//! and a node's [`Consumption`] is complete by the time the walk reaches
//! it. Visiting a node does two things:
//!
//! 1. Decide the node's guard from its own [`Consumption`]: when every
//! consumer reads the output only when X returned rows, the node gets
//! guard `non_empty(X)`.
//!
//! 2. Update the [`Consumption`] of each input the node reads, from the
//! operation's declared [`InputRead`]s. A [`PerRowOf`](InputRead::PerRowOf)
//! input contributes "read only when the iterated node returned rows"
//! (rule 1). An [`Always`](InputRead::Always) input is read whenever the
//! node itself runs, so it contributes the node's own `non_empty` guard —
//! or "read regardless" when the node has none (rule 2).
//!
//! In the example, the walk visits n3, n2, n1, n0. n3 declares n2 as
//! `PerRowOf(n0)`, so visiting n3 sets n2's [`Consumption`] to "read only
//! when n0 returned rows". Visiting n2 turns that into guard `non_empty(n0)`,
//! and — since n2 now carries that guard — sets n1's [`Consumption`] to the
//! same state. Visiting n1 turns it into the same guard.
//!
//! The pass runs once, at `LogicalPlan::new`, after planning has produced
//! the complete graph. Guards cannot be assigned earlier, while statements
//! are still being planned: a guard is only valid if every consumer agrees,
//! and planning may still add a consumer that reads the node
//! unconditionally.
use IndexVec;
use ;
/// The merge, over a node's already-visited consumers, of how each one
/// consumes the node's output.
pub