1use rudb_common::{Error, LogicalType, Result, Value};
36use rudb_vector::{Buffer, Data, Form, Validity, Vector};
37
38use crate::fallback::{self, Kernel};
39use crate::shape::{identity, nulls_of};
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub enum Connective {
44 And,
46 Or,
48}
49
50pub fn combine(op: Connective, children: &[Vector]) -> Result<Vector> {
56 let first =
57 children.first().ok_or_else(|| Error::internal("a conjunction with no children"))?;
58 let rows = first.len();
59 for (at, child) in children.iter().enumerate() {
60 if child.len() != rows {
61 return Err(Error::internal(format!(
62 "child {at} of a conjunction is {} rows and child 0 is {rows}",
63 child.len()
64 )));
65 }
66 }
67 if let Some(vector) = folded(op, children, rows) {
68 return Ok(vector);
69 }
70 let left = first.form();
71 fallback::record(Kernel::Logic, left, children.get(1).map_or(left, Vector::form));
72 let mut values = Vec::with_capacity(rows);
73 for index in 0..rows {
76 let mut answer = Some(matches!(op, Connective::And));
77 for child in children {
78 let held = match child.value_at(index) {
79 Value::Boolean(held) => Some(held),
80 Value::Null => None,
81 other => {
82 return Err(Error::internal(format!(
83 "a conjunction over a {} value",
84 other.logical_type()
85 )));
86 }
87 };
88 answer = fold(op, answer, held);
89 }
90 values.push(match answer {
91 Some(held) => Value::Boolean(held),
92 None => Value::Null,
93 });
94 }
95 Vector::from_values(LogicalType::Boolean, &values)
96}
97
98fn folded(op: Connective, children: &[Vector], rows: usize) -> Option<Vector> {
105 match op {
106 Connective::And => fold_runs::<false>(children, rows),
107 Connective::Or => fold_runs::<true>(children, rows),
108 }
109}
110
111fn fold_runs<const DOMINANT: bool>(children: &[Vector], rows: usize) -> Option<Vector> {
115 if rows == 0 {
116 return Vector::flat(LogicalType::Boolean, Data::Bool(Buffer::new())).ok();
121 }
122 if children.iter().any(|child| child.logical_type() != &LogicalType::Boolean) {
126 return None;
127 }
128
129 let mut decided = vec![false; rows];
130 let mut unknown = vec![false; rows];
131 let mut nullable = false;
132
133 for child in children {
134 let nulls = nulls_of(child);
135 nullable |= nulls.has_nulls(rows);
136 match child.form() {
137 Form::Constant => match child.value_at(0) {
138 Value::Boolean(held) if held == DOMINANT => decided.fill(true),
139 Value::Boolean(_) => {}
140 Value::Null => unknown.fill(true),
141 _ => return None,
142 },
143 Form::Flat => {
144 let Some(Data::Bool(values)) = child.data() else {
145 return None;
146 };
147 if values.len() < rows {
148 return None;
149 }
150 absorb::<DOMINANT, _>(values, identity, &nulls, &mut decided, &mut unknown);
151 }
152 Form::Dictionary => {
153 let (codes, values) = child.dictionary_parts()?;
154 let Some(Data::Bool(held)) = values.data() else {
155 return None;
156 };
157 if codes.len() < rows {
158 return None;
159 }
160 absorb::<DOMINANT, _>(
161 held,
162 |index| codes[index] as usize,
163 &nulls,
164 &mut decided,
165 &mut unknown,
166 );
167 }
168 _ => return None,
169 }
170 }
171
172 let validity = if nullable {
175 let live: Vec<bool> =
178 decided.iter().zip(&unknown).map(|(&hit, &null)| hit || !null).collect();
179 Validity::from_run(&live)
180 } else {
181 Validity::AllValid
182 };
183 let data = if DOMINANT {
184 decided
185 } else {
186 decided.iter().zip(&unknown).map(|(&hit, &null)| !(hit | null)).collect()
189 };
190 Some(Vector::flat(LogicalType::Boolean, Data::Bool(data.into())).ok()?.with_validity(validity))
191}
192
193fn absorb<const DOMINANT: bool, M: Fn(usize) -> usize>(
200 values: &[bool],
201 at: M,
202 nulls: &Validity,
203 decided: &mut [bool],
204 unknown: &mut [bool],
205) {
206 match nulls {
207 Validity::AllValid => {
208 for (index, slot) in decided.iter_mut().enumerate() {
209 *slot |= values[at(index)] == DOMINANT;
210 }
211 }
212 Validity::AllInvalid => unknown.fill(true),
214 Validity::Mask(mask) => {
215 for (word_at, (hits, nulls)) in
218 decided.chunks_mut(64).zip(unknown.chunks_mut(64)).enumerate()
219 {
220 let word = mask.word(word_at);
221 let base = word_at * 64;
222 for (bit, (hit, null)) in hits.iter_mut().zip(nulls.iter_mut()).enumerate() {
223 let valid = word >> bit & 1 == 1;
224 *hit |= valid & (values[at(base + bit)] == DOMINANT);
227 *null |= !valid;
228 }
229 }
230 }
231 }
232}
233
234fn fold(op: Connective, left: Option<bool>, right: Option<bool>) -> Option<bool> {
240 match op {
241 Connective::And => match (left, right) {
242 (Some(false), _) | (_, Some(false)) => Some(false),
243 (Some(true), Some(true)) => Some(true),
244 _ => None,
245 },
246 Connective::Or => match (left, right) {
247 (Some(true), _) | (_, Some(true)) => Some(true),
248 (Some(false), Some(false)) => Some(false),
249 _ => None,
250 },
251 }
252}
253
254#[must_use]
259pub fn is_true(value: &Value) -> bool {
260 matches!(value, Value::Boolean(true))
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 fn vector(values: &[Value]) -> Vector {
268 Vector::from_values(LogicalType::Boolean, values).expect("booleans")
269 }
270
271 const TRUE: Value = Value::Boolean(true);
272 const FALSE: Value = Value::Boolean(false);
273
274 #[test]
275 fn a_false_wins_an_and_even_against_an_unknown() {
276 let result = combine(Connective::And, &[vector(&[Value::Null]), vector(&[FALSE])])
277 .expect("two booleans");
278 assert_eq!(result.value_at(0), FALSE);
279 }
280
281 #[test]
282 fn a_true_wins_an_or_even_against_an_unknown() {
283 let result = combine(Connective::Or, &[vector(&[Value::Null]), vector(&[TRUE])])
284 .expect("two booleans");
285 assert_eq!(result.value_at(0), TRUE);
286 }
287
288 #[test]
289 fn an_unknown_survives_when_nothing_decides_it() {
290 let result = combine(Connective::And, &[vector(&[Value::Null]), vector(&[TRUE])])
291 .expect("two booleans");
292 assert_eq!(result.value_at(0), Value::Null);
293 let result = combine(Connective::Or, &[vector(&[Value::Null]), vector(&[FALSE])])
294 .expect("two booleans");
295 assert_eq!(result.value_at(0), Value::Null);
296 }
297
298 #[test]
299 fn a_flat_conjunction_of_more_than_two_children_is_one_pass() {
300 let result = combine(
301 Connective::And,
302 &[vector(&[TRUE]), vector(&[TRUE]), vector(&[TRUE]), vector(&[FALSE])],
303 )
304 .expect("four booleans");
305 assert_eq!(result.value_at(0), FALSE);
306 }
307
308 #[test]
309 fn a_where_clause_drops_the_rows_it_cannot_decide() {
310 assert!(is_true(&TRUE));
311 assert!(!is_true(&FALSE));
312 assert!(!is_true(&Value::Null));
313 }
314
315 #[test]
316 fn a_conjunction_with_no_children_is_caught() {
317 let error = combine(Connective::And, &[]).expect_err("nothing to combine");
318 assert!(error.message().contains("no children"), "{error}");
319 }
320
321 fn oracle(op: Connective, children: &[Vector]) -> Result<Vector> {
327 let rows = children.first().map_or(0, Vector::len);
328 let mut values = Vec::with_capacity(rows);
329 for index in 0..rows {
330 let mut answer = Some(matches!(op, Connective::And));
331 for child in children {
332 let held = match child.value_at(index) {
333 Value::Boolean(held) => Some(held),
334 Value::Null => None,
335 other => {
336 return Err(Error::internal(format!(
337 "a conjunction over a {} value",
338 other.logical_type()
339 )));
340 }
341 };
342 answer = fold(op, answer, held);
343 }
344 values.push(match answer {
345 Some(held) => Value::Boolean(held),
346 None => Value::Null,
347 });
348 }
349 Vector::from_values(LogicalType::Boolean, &values)
350 }
351
352 fn agrees(op: Connective, children: &[Vector]) {
353 let fast = combine(op, children);
354 let slow = oracle(op, children);
355 match (fast, slow) {
356 (Ok(fast), Ok(slow)) => assert_eq!(fast, slow, "{op:?} over {children:?}"),
357 (Err(fast), Err(slow)) => {
358 assert_eq!(fast.message(), slow.message(), "{op:?} over {children:?}");
359 }
360 (fast, slow) => panic!("{op:?} over {children:?} gave {fast:?} and {slow:?}"),
361 }
362 }
363
364 struct Rng(u64);
366
367 impl Rng {
368 fn next(&mut self) -> u64 {
369 self.0 ^= self.0 << 13;
370 self.0 ^= self.0 >> 7;
371 self.0 ^= self.0 << 17;
372 self.0
373 }
374 }
375
376 fn sample(rng: &mut Rng, rows: usize, nulls: u64) -> Vector {
378 let values: Vec<Value> = (0..rows)
379 .map(|_| {
380 let draw = rng.next();
381 if nulls > 0 && draw % nulls == 0 {
382 Value::Null
383 } else {
384 Value::Boolean(draw % 2 == 0)
385 }
386 })
387 .collect();
388 vector(&values)
389 }
390
391 #[test]
392 fn every_form_and_null_density_agrees_with_the_row_at_a_time_path() {
393 let mut rng = Rng(0x5eed_1eaf_c0ff_ee01);
394 let rows = 97;
395 for op in [Connective::And, Connective::Or] {
396 for nulls in [0, 2, 7] {
397 let flat = sample(&mut rng, rows, nulls);
398 let other = sample(&mut rng, rows, nulls);
399 let third = sample(&mut rng, rows, nulls);
400
401 agrees(op, &[flat.clone(), other.clone()]);
403 agrees(op, &[flat.clone(), other.clone(), third.clone()]);
405 agrees(op, std::slice::from_ref(&flat));
407
408 for held in [TRUE, FALSE, Value::Null] {
410 let constant = Vector::constant(LogicalType::Boolean, held, rows);
411 agrees(op, &[flat.clone(), constant.clone()]);
412 agrees(op, &[constant.clone(), flat.clone()]);
413 agrees(op, &[constant.clone(), flat.clone(), other.clone()]);
414 }
415
416 let dictionary = Vector::dictionary(
419 (0..rows)
420 .map(|index| u32::try_from(index % 3).expect("a code under three"))
421 .collect(),
422 vector(&[TRUE, FALSE, Value::Null]),
423 )
424 .expect("three codes into three values");
425 agrees(op, &[dictionary.clone(), flat.clone()]);
426 agrees(op, &[flat.clone(), dictionary.clone()]);
427 agrees(op, &[dictionary.clone(), dictionary.clone()]);
428 }
429 }
430 }
431
432 #[test]
433 fn a_child_that_is_all_null_still_lets_a_decided_row_through() {
434 let rows = 8;
437 let gone = Vector::constant(LogicalType::Boolean, Value::Null, rows);
438 let mixed = vector(&[TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE]);
439 agrees(Connective::And, &[gone.clone(), mixed.clone()]);
440 agrees(Connective::Or, &[gone.clone(), mixed.clone()]);
441 let result = combine(Connective::And, &[gone, mixed]).expect("two booleans");
442 assert_eq!(result.value_at(0), Value::Null);
443 assert_eq!(result.value_at(1), FALSE);
444 }
445
446 #[test]
447 fn an_empty_conjunction_of_empty_children_is_an_empty_answer() {
448 let empty = vector(&[]);
449 agrees(Connective::And, &[empty.clone(), empty.clone()]);
450 agrees(Connective::Or, &[empty.clone(), empty]);
451 }
452
453 #[test]
454 fn a_child_that_is_not_boolean_is_still_caught_by_name() {
455 let numbers = Vector::from_values(
456 LogicalType::Integer,
457 &[Value::Integer(1), Value::Integer(0), Value::Integer(3)],
458 )
459 .expect("integers");
460 let error = combine(Connective::And, &[vector(&[TRUE, TRUE, TRUE]), numbers])
461 .expect_err("a conjunction over integers");
462 assert!(error.message().contains("conjunction over"), "{error}");
463 assert!(error.message().contains("INTEGER"), "{error}");
464 }
465
466 #[test]
467 fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
468 let _turn = fallback::TURN.lock().expect("no test panics while holding this");
471 let before = fallback::count(Kernel::Logic, Form::Sequence, Form::Flat);
472 let rows = 4;
473 let ids = Vector::sequence(0, 1, rows);
474 let flat = vector(&[TRUE, FALSE, TRUE, FALSE]);
475 let error = combine(Connective::And, &[ids, flat]).expect_err("a conjunction over bigints");
478 assert!(error.message().contains("conjunction over"), "{error}");
479 assert!(fallback::count(Kernel::Logic, Form::Sequence, Form::Flat) > before);
480 }
481
482 #[test]
483 fn a_children_length_mismatch_names_the_child_that_is_wrong() {
484 let error = combine(Connective::And, &[vector(&[TRUE, TRUE]), vector(&[TRUE])])
485 .expect_err("two lengths");
486 assert!(error.message().contains("child 1"), "{error}");
487 }
488}