1use std::collections::HashMap;
17
18use rudb_plan::{Expr, ExprRef, Node, NodeRef, Plan};
19
20#[derive(Debug, Clone, Default, PartialEq, Eq)]
22pub struct TableSet {
23 words: Vec<u64>,
24}
25
26impl TableSet {
27 #[must_use]
29 pub fn new() -> Self {
30 Self::default()
31 }
32
33 #[must_use]
35 pub fn of(index: u32) -> Self {
36 let mut set = Self::new();
37 set.insert(index);
38 set
39 }
40
41 pub fn insert(&mut self, index: u32) {
43 let (word, bit) = place(index);
44 if self.words.len() <= word {
45 self.words.resize(word + 1, 0);
46 }
47 self.words[word] |= bit;
48 }
49
50 #[must_use]
52 pub fn contains(&self, index: u32) -> bool {
53 let (word, bit) = place(index);
54 self.words.get(word).is_some_and(|held| held & bit != 0)
55 }
56
57 #[must_use]
59 pub fn is_empty(&self) -> bool {
60 self.words.iter().all(|word| *word == 0)
61 }
62
63 pub fn extend(&mut self, other: &Self) {
65 if self.words.len() < other.words.len() {
66 self.words.resize(other.words.len(), 0);
67 }
68 for (held, word) in self.words.iter_mut().zip(&other.words) {
69 *held |= word;
70 }
71 }
72
73 #[must_use]
79 pub fn is_subset_of(&self, other: &Self) -> bool {
80 self.words
81 .iter()
82 .enumerate()
83 .all(|(at, word)| word & !other.words.get(at).copied().unwrap_or(0) == 0)
84 }
85}
86
87fn place(index: u32) -> (usize, u64) {
89 let index = index as usize;
90 (index / 64, 1 << (index % 64))
91}
92
93#[derive(Debug, Default)]
100pub struct Tables {
101 known: HashMap<ExprRef, TableSet>,
102}
103
104impl Tables {
105 #[must_use]
107 pub fn new() -> Self {
108 Self::default()
109 }
110
111 pub fn of(&mut self, plan: &Plan, expr: ExprRef) -> TableSet {
117 if let Some(known) = self.known.get(&expr) {
118 return known.clone();
119 }
120 let mut set = TableSet::new();
121 match *plan.expr(expr) {
122 Expr::Column(binding) => set.insert(binding.table),
123 Expr::Constant(_) => {}
124 Expr::Cast { input, .. } => set.extend(&self.of(plan, input)),
125 Expr::Compare { left, right, .. } => {
126 set.extend(&self.of(plan, left));
127 set.extend(&self.of(plan, right));
128 }
129 Expr::Conjunction { children, .. } | Expr::Function { args: children, .. } => {
130 for child in plan.expr_list(children).to_vec() {
131 set.extend(&self.of(plan, child));
132 }
133 }
134 Expr::Aggregate { args, filter, .. } => {
135 for arg in plan.expr_list(args).to_vec() {
136 set.extend(&self.of(plan, arg));
137 }
138 if let Some(inner) = filter {
139 set.extend(&self.of(plan, inner));
140 }
141 }
142 Expr::Case { arms, otherwise } => {
143 for arm in plan.arm_list(arms).to_vec() {
144 set.extend(&self.of(plan, arm.when));
145 set.extend(&self.of(plan, arm.then));
146 }
147 if let Some(inner) = otherwise {
148 set.extend(&self.of(plan, inner));
149 }
150 }
151 }
152 self.known.insert(expr, set.clone());
153 set
154 }
155}
156
157#[must_use]
163pub fn produced(plan: &Plan, at: NodeRef) -> TableSet {
164 let mut set = TableSet::new();
165 collect(plan, at, &mut set);
166 set
167}
168
169fn collect(plan: &Plan, at: NodeRef, set: &mut TableSet) {
171 match *plan.node(at) {
172 Node::Get { index, .. }
173 | Node::Values { index, .. }
174 | Node::TableFunction { index, .. }
175 | Node::Project { index, .. }
176 | Node::Aggregate { index, .. }
177 | Node::SetOp { index, .. } => set.insert(index),
178 Node::Dummy => {}
179 Node::Filter { input, .. }
180 | Node::Sort { input, .. }
181 | Node::Limit { input, .. }
182 | Node::TopN { input, .. }
183 | Node::Distinct { input, .. } => collect(plan, input, set),
184 Node::Join { left, right, .. } | Node::CrossProduct { left, right } => {
185 collect(plan, left, set);
186 collect(plan, right, set);
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::{TableSet, Tables, produced};
194 use rudb_plan::Plan;
195
196 #[test]
197 fn a_set_holds_what_was_put_in_it_and_grows_past_one_word() {
198 let mut set = TableSet::new();
199 assert!(set.is_empty());
200 set.insert(0);
201 set.insert(200);
202 assert!(set.contains(0));
203 assert!(set.contains(200));
204 assert!(!set.contains(1));
205 assert!(!set.contains(199));
206 assert!(!set.is_empty());
207 }
208
209 #[test]
210 fn the_empty_set_is_a_subset_of_everything_including_itself() {
211 let empty = TableSet::new();
212 assert!(empty.is_subset_of(&empty));
213 assert!(empty.is_subset_of(&TableSet::of(3)));
214 assert!(!TableSet::of(3).is_subset_of(&empty));
215 }
216
217 #[test]
218 fn containment_holds_across_the_word_boundary() {
219 let mut wide = TableSet::of(1);
220 wide.insert(100);
221 assert!(TableSet::of(100).is_subset_of(&wide));
222 assert!(!TableSet::of(101).is_subset_of(&wide));
223 assert!(wide.is_subset_of(&wide));
224
225 let mut narrow = TableSet::of(1);
226 assert!(narrow.is_subset_of(&wide));
227 narrow.extend(&TableSet::of(100));
228 assert_eq!(narrow, wide);
229 }
230
231 const JOIN: &str = "\
232Join INNER on=[(#0.0::INTEGER = #1.0::INTEGER)::BOOLEAN]
233 Get memory.main.t AS a #0 [a::INTEGER]
234 Get memory.main.t AS b #1 [a::INTEGER]
235";
236
237 #[test]
238 fn a_condition_that_reads_both_sides_reads_both_table_indices() {
239 let plan = Plan::parse(JOIN).expect("a join");
240 let condition = match *plan.node(plan.root()) {
241 rudb_plan::Node::Join { conditions, .. } => plan.expr_list(conditions)[0],
242 _ => unreachable!("the root is the join"),
243 };
244 let mut tables = Tables::new();
245 let read = tables.of(&plan, condition);
246 assert!(read.contains(0));
247 assert!(read.contains(1));
248 assert_eq!(tables.of(&plan, condition), read);
250 }
251
252 #[test]
253 fn a_join_produces_both_sides_and_a_projection_produces_only_itself() {
254 let plan = Plan::parse(JOIN).expect("a join");
255 let both = produced(&plan, plan.root());
256 assert!(both.contains(0));
257 assert!(both.contains(1));
258
259 let text = "\
260Project #2 [#0.0::INTEGER AS x]
261 Get memory.main.t AS t #0 [a::INTEGER]
262";
263 let plan = Plan::parse(text).expect("a projection");
264 let visible = produced(&plan, plan.root());
265 assert!(visible.contains(2));
266 assert!(!visible.contains(0), "nothing above a projection can name what is under it");
267 }
268}