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<V: AsRef<Vector>>(op: Connective, children: &[V]) -> Result<Vector> {
62 let first = children
63 .first()
64 .map(AsRef::as_ref)
65 .ok_or_else(|| Error::internal("a conjunction with no children"))?;
66 let rows = first.len();
67 for (at, child) in children.iter().enumerate() {
68 if child.as_ref().len() != rows {
69 return Err(Error::internal(format!(
70 "child {at} of a conjunction is {} rows and child 0 is {rows}",
71 child.as_ref().len()
72 )));
73 }
74 }
75 if let Some(vector) = folded(op, children, rows) {
76 return Ok(vector);
77 }
78 let left = first.form();
79 fallback::record(Kernel::Logic, left, children.get(1).map_or(left, |c| c.as_ref().form()));
80 let mut values = Vec::with_capacity(rows);
81 for index in 0..rows {
84 let mut answer = Some(matches!(op, Connective::And));
85 for child in children {
86 let held = match child.as_ref().value_at(index) {
87 Value::Boolean(held) => Some(held),
88 Value::Null => None,
89 other => {
90 return Err(Error::internal(format!(
91 "a conjunction over a {} value",
92 other.logical_type()
93 )));
94 }
95 };
96 answer = fold(op, answer, held);
97 }
98 values.push(match answer {
99 Some(held) => Value::Boolean(held),
100 None => Value::Null,
101 });
102 }
103 Vector::from_values(LogicalType::Boolean, &values)
104}
105
106fn folded<V: AsRef<Vector>>(op: Connective, children: &[V], rows: usize) -> Option<Vector> {
113 match op {
114 Connective::And => fold_runs::<false, _>(children, rows),
115 Connective::Or => fold_runs::<true, _>(children, rows),
116 }
117}
118
119fn fold_runs<const DOMINANT: bool, V: AsRef<Vector>>(
123 children: &[V],
124 rows: usize,
125) -> Option<Vector> {
126 if rows == 0 {
127 return Vector::flat(LogicalType::Boolean, Data::Bool(Buffer::new())).ok();
132 }
133 if children.iter().any(|child| child.as_ref().logical_type() != &LogicalType::Boolean) {
137 return None;
138 }
139
140 let mut decided = vec![false; rows];
141 let mut unknown = vec![false; rows];
142 let mut nullable = false;
143
144 for child in children {
145 let child = child.as_ref();
146 let nulls = nulls_of(child);
147 nullable |= nulls.has_nulls(rows);
148 match child.form() {
149 Form::Constant => match child.value_at(0) {
150 Value::Boolean(held) if held == DOMINANT => decided.fill(true),
151 Value::Boolean(_) => {}
152 Value::Null => unknown.fill(true),
153 _ => return None,
154 },
155 Form::Flat => {
156 let Some(Data::Bool(values)) = child.data() else {
157 return None;
158 };
159 if values.len() < rows {
160 return None;
161 }
162 absorb::<DOMINANT, _>(values, identity, &nulls, &mut decided, &mut unknown);
163 }
164 Form::Dictionary => {
165 let (codes, values) = child.dictionary_parts()?;
166 let Some(Data::Bool(held)) = values.data() else {
167 return None;
168 };
169 if codes.len() < rows {
170 return None;
171 }
172 absorb::<DOMINANT, _>(
173 held,
174 |index| codes[index] as usize,
175 &nulls,
176 &mut decided,
177 &mut unknown,
178 );
179 }
180 _ => return None,
181 }
182 }
183
184 let validity = if nullable {
187 let live: Vec<bool> =
190 decided.iter().zip(&unknown).map(|(&hit, &null)| hit || !null).collect();
191 Validity::from_run(&live)
192 } else {
193 Validity::AllValid
194 };
195 let data = if DOMINANT {
196 decided
197 } else {
198 decided.iter().zip(&unknown).map(|(&hit, &null)| !(hit | null)).collect()
201 };
202 Some(Vector::flat(LogicalType::Boolean, Data::Bool(data.into())).ok()?.with_validity(validity))
203}
204
205fn absorb<const DOMINANT: bool, M: Fn(usize) -> usize>(
212 values: &[bool],
213 at: M,
214 nulls: &Validity,
215 decided: &mut [bool],
216 unknown: &mut [bool],
217) {
218 match nulls {
219 Validity::AllValid => {
220 for (index, slot) in decided.iter_mut().enumerate() {
221 *slot |= values[at(index)] == DOMINANT;
222 }
223 }
224 Validity::AllInvalid => unknown.fill(true),
226 Validity::Mask(mask) => {
227 for (word_at, (hits, nulls)) in
230 decided.chunks_mut(64).zip(unknown.chunks_mut(64)).enumerate()
231 {
232 let word = mask.word(word_at);
233 let base = word_at * 64;
234 for (bit, (hit, null)) in hits.iter_mut().zip(nulls.iter_mut()).enumerate() {
235 let valid = word >> bit & 1 == 1;
236 *hit |= valid & (values[at(base + bit)] == DOMINANT);
239 *null |= !valid;
240 }
241 }
242 }
243 }
244}
245
246fn fold(op: Connective, left: Option<bool>, right: Option<bool>) -> Option<bool> {
252 match op {
253 Connective::And => match (left, right) {
254 (Some(false), _) | (_, Some(false)) => Some(false),
255 (Some(true), Some(true)) => Some(true),
256 _ => None,
257 },
258 Connective::Or => match (left, right) {
259 (Some(true), _) | (_, Some(true)) => Some(true),
260 (Some(false), Some(false)) => Some(false),
261 _ => None,
262 },
263 }
264}
265
266#[must_use]
271pub fn is_true(value: &Value) -> bool {
272 matches!(value, Value::Boolean(true))
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 fn vector(values: &[Value]) -> Vector {
280 Vector::from_values(LogicalType::Boolean, values).expect("booleans")
281 }
282
283 const TRUE: Value = Value::Boolean(true);
284 const FALSE: Value = Value::Boolean(false);
285
286 #[test]
287 fn a_false_wins_an_and_even_against_an_unknown() {
288 let result = combine(Connective::And, &[vector(&[Value::Null]), vector(&[FALSE])])
289 .expect("two booleans");
290 assert_eq!(result.value_at(0), FALSE);
291 }
292
293 #[test]
294 fn a_true_wins_an_or_even_against_an_unknown() {
295 let result = combine(Connective::Or, &[vector(&[Value::Null]), vector(&[TRUE])])
296 .expect("two booleans");
297 assert_eq!(result.value_at(0), TRUE);
298 }
299
300 #[test]
301 fn an_unknown_survives_when_nothing_decides_it() {
302 let result = combine(Connective::And, &[vector(&[Value::Null]), vector(&[TRUE])])
303 .expect("two booleans");
304 assert_eq!(result.value_at(0), Value::Null);
305 let result = combine(Connective::Or, &[vector(&[Value::Null]), vector(&[FALSE])])
306 .expect("two booleans");
307 assert_eq!(result.value_at(0), Value::Null);
308 }
309
310 #[test]
311 fn a_flat_conjunction_of_more_than_two_children_is_one_pass() {
312 let result = combine(
313 Connective::And,
314 &[vector(&[TRUE]), vector(&[TRUE]), vector(&[TRUE]), vector(&[FALSE])],
315 )
316 .expect("four booleans");
317 assert_eq!(result.value_at(0), FALSE);
318 }
319
320 #[test]
321 fn a_where_clause_drops_the_rows_it_cannot_decide() {
322 assert!(is_true(&TRUE));
323 assert!(!is_true(&FALSE));
324 assert!(!is_true(&Value::Null));
325 }
326
327 #[test]
328 fn a_conjunction_with_no_children_is_caught() {
329 let nothing: &[Vector] = &[];
330 let error = combine(Connective::And, nothing).expect_err("nothing to combine");
331 assert!(error.message().contains("no children"), "{error}");
332 }
333
334 fn oracle(op: Connective, children: &[Vector]) -> Result<Vector> {
340 let rows = children.first().map_or(0, Vector::len);
341 let mut values = Vec::with_capacity(rows);
342 for index in 0..rows {
343 let mut answer = Some(matches!(op, Connective::And));
344 for child in children {
345 let held = match child.value_at(index) {
346 Value::Boolean(held) => Some(held),
347 Value::Null => None,
348 other => {
349 return Err(Error::internal(format!(
350 "a conjunction over a {} value",
351 other.logical_type()
352 )));
353 }
354 };
355 answer = fold(op, answer, held);
356 }
357 values.push(match answer {
358 Some(held) => Value::Boolean(held),
359 None => Value::Null,
360 });
361 }
362 Vector::from_values(LogicalType::Boolean, &values)
363 }
364
365 fn agrees(op: Connective, children: &[Vector]) {
366 let fast = combine(op, children);
367 let slow = oracle(op, children);
368 match (fast, slow) {
369 (Ok(fast), Ok(slow)) => assert_eq!(fast, slow, "{op:?} over {children:?}"),
370 (Err(fast), Err(slow)) => {
371 assert_eq!(fast.message(), slow.message(), "{op:?} over {children:?}");
372 }
373 (fast, slow) => panic!("{op:?} over {children:?} gave {fast:?} and {slow:?}"),
374 }
375 }
376
377 struct Rng(u64);
379
380 impl Rng {
381 fn next(&mut self) -> u64 {
382 self.0 ^= self.0 << 13;
383 self.0 ^= self.0 >> 7;
384 self.0 ^= self.0 << 17;
385 self.0
386 }
387 }
388
389 fn sample(rng: &mut Rng, rows: usize, nulls: u64) -> Vector {
391 let values: Vec<Value> = (0..rows)
392 .map(|_| {
393 let draw = rng.next();
394 if nulls > 0 && draw % nulls == 0 {
395 Value::Null
396 } else {
397 Value::Boolean(draw % 2 == 0)
398 }
399 })
400 .collect();
401 vector(&values)
402 }
403
404 #[test]
405 fn every_form_and_null_density_agrees_with_the_row_at_a_time_path() {
406 let mut rng = Rng(0x5eed_1eaf_c0ff_ee01);
407 let rows = 97;
408 for op in [Connective::And, Connective::Or] {
409 for nulls in [0, 2, 7] {
410 let flat = sample(&mut rng, rows, nulls);
411 let other = sample(&mut rng, rows, nulls);
412 let third = sample(&mut rng, rows, nulls);
413
414 agrees(op, &[flat.clone(), other.clone()]);
416 agrees(op, &[flat.clone(), other.clone(), third.clone()]);
418 agrees(op, std::slice::from_ref(&flat));
420
421 for held in [TRUE, FALSE, Value::Null] {
423 let constant = Vector::constant(LogicalType::Boolean, held, rows);
424 agrees(op, &[flat.clone(), constant.clone()]);
425 agrees(op, &[constant.clone(), flat.clone()]);
426 agrees(op, &[constant.clone(), flat.clone(), other.clone()]);
427 }
428
429 let dictionary = Vector::dictionary(
432 (0..rows)
433 .map(|index| u32::try_from(index % 3).expect("a code under three"))
434 .collect(),
435 vector(&[TRUE, FALSE, Value::Null]),
436 )
437 .expect("three codes into three values");
438 agrees(op, &[dictionary.clone(), flat.clone()]);
439 agrees(op, &[flat.clone(), dictionary.clone()]);
440 agrees(op, &[dictionary.clone(), dictionary.clone()]);
441 }
442 }
443 }
444
445 #[test]
446 fn a_child_that_is_all_null_still_lets_a_decided_row_through() {
447 let rows = 8;
450 let gone = Vector::constant(LogicalType::Boolean, Value::Null, rows);
451 let mixed = vector(&[TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE]);
452 agrees(Connective::And, &[gone.clone(), mixed.clone()]);
453 agrees(Connective::Or, &[gone.clone(), mixed.clone()]);
454 let result = combine(Connective::And, &[gone, mixed]).expect("two booleans");
455 assert_eq!(result.value_at(0), Value::Null);
456 assert_eq!(result.value_at(1), FALSE);
457 }
458
459 #[test]
460 fn an_empty_conjunction_of_empty_children_is_an_empty_answer() {
461 let empty = vector(&[]);
462 agrees(Connective::And, &[empty.clone(), empty.clone()]);
463 agrees(Connective::Or, &[empty.clone(), empty]);
464 }
465
466 #[test]
467 fn a_child_that_is_not_boolean_is_still_caught_by_name() {
468 let numbers = Vector::from_values(
469 LogicalType::Integer,
470 &[Value::Integer(1), Value::Integer(0), Value::Integer(3)],
471 )
472 .expect("integers");
473 let error = combine(Connective::And, &[vector(&[TRUE, TRUE, TRUE]), numbers])
474 .expect_err("a conjunction over integers");
475 assert!(error.message().contains("conjunction over"), "{error}");
476 assert!(error.message().contains("INTEGER"), "{error}");
477 }
478
479 #[test]
480 fn a_form_pair_with_no_loop_is_still_right_and_says_so() {
481 let before = fallback::count(Kernel::Logic, Form::Sequence, Form::Flat);
483 let rows = 4;
484 let ids = Vector::sequence(0, 1, rows);
485 let flat = vector(&[TRUE, FALSE, TRUE, FALSE]);
486 let error = combine(Connective::And, &[ids, flat]).expect_err("a conjunction over bigints");
489 assert!(error.message().contains("conjunction over"), "{error}");
490 assert!(fallback::count(Kernel::Logic, Form::Sequence, Form::Flat) > before);
491 }
492
493 #[test]
494 fn a_children_length_mismatch_names_the_child_that_is_wrong() {
495 let error = combine(Connective::And, &[vector(&[TRUE, TRUE]), vector(&[TRUE])])
496 .expect_err("two lengths");
497 assert!(error.message().contains("child 1"), "{error}");
498 }
499}