reifydb_evaluate/expression/
logic.rs1use reifydb_core::value::column::{ColumnWithName, buffer::ColumnBuffer};
5use reifydb_value::{
6 error::{LogicalOp, OperandCategory, TypeError},
7 fragment::Fragment,
8 util::bitvec::BitVec,
9};
10
11use super::option::apply_option_bitvec;
12use crate::Result;
13
14pub(crate) fn try_short_circuit_and(
15 l: &ColumnWithName,
16 fragment: &Fragment,
17 row_count: usize,
18) -> Option<ColumnWithName> {
19 if is_all_false_defined(l.data()) {
20 Some(ColumnWithName::new(fragment.clone(), ColumnBuffer::bool(vec![false; row_count])))
21 } else {
22 None
23 }
24}
25
26pub(crate) fn try_short_circuit_or(
27 l: &ColumnWithName,
28 fragment: &Fragment,
29 row_count: usize,
30) -> Option<ColumnWithName> {
31 if is_all_true_defined(l.data()) {
32 Some(ColumnWithName::new(fragment.clone(), ColumnBuffer::bool(vec![true; row_count])))
33 } else {
34 None
35 }
36}
37
38fn is_all_false_defined(buffer: &ColumnBuffer) -> bool {
39 match buffer {
40 ColumnBuffer::Bool(c) => !c.is_empty() && c.data().none(),
41 ColumnBuffer::Option {
42 inner,
43 bitvec,
44 } => {
45 if !bitvec.all_ones() {
46 return false;
47 }
48 matches!(inner.as_ref(), ColumnBuffer::Bool(c) if !c.is_empty() && c.data().none())
49 }
50 _ => false,
51 }
52}
53
54fn is_all_true_defined(buffer: &ColumnBuffer) -> bool {
55 match buffer {
56 ColumnBuffer::Bool(c) => !c.is_empty() && c.data().all_ones(),
57 ColumnBuffer::Option {
58 inner,
59 bitvec,
60 } => {
61 if !bitvec.all_ones() {
62 return false;
63 }
64 matches!(inner.as_ref(), ColumnBuffer::Bool(c) if !c.is_empty() && c.data().all_ones())
65 }
66 _ => false,
67 }
68}
69
70fn is_all_none(bv: Option<&BitVec>) -> bool {
71 match bv {
72 Some(bv) => bv.count_ones() == 0,
73 None => false,
74 }
75}
76
77pub fn execute_logical_op(
78 left: &ColumnWithName,
79 right: &ColumnWithName,
80 fragment: &Fragment,
81 logical_op: LogicalOp,
82 bool_fn: fn(bool, bool) -> bool,
83) -> Result<ColumnWithName> {
84 let (left_data, left_bv) = left.data().unwrap_option();
85 let (right_data, right_bv) = right.data().unwrap_option();
86 let len = left_data.len();
87
88 let synthetic = BitVec::repeat(len, false);
89
90 let (l_v_bits, l_valid_bv) = match left_data {
91 ColumnBuffer::Bool(c) => (c.data(), left_bv),
92 _ if is_all_none(left_bv) => (&synthetic, Some(&synthetic)),
93 _ => return type_error(&logical_op, fragment, left_data, right_data),
94 };
95 let (r_v_bits, r_valid_bv) = match right_data {
96 ColumnBuffer::Bool(c) => (c.data(), right_bv),
97 _ if is_all_none(right_bv) => (&synthetic, Some(&synthetic)),
98 _ => return type_error(&logical_op, fragment, left_data, right_data),
99 };
100
101 let value_data: Vec<bool> =
102 l_v_bits.iter().zip(r_v_bits.iter()).map(|(l_val, r_val)| bool_fn(l_val, r_val)).collect();
103 let value_buffer = ColumnBuffer::bool(value_data);
104
105 let result_bv = compute_kleene_validity(&logical_op, l_valid_bv, r_valid_bv, l_v_bits, r_v_bits, len);
106
107 let final_buffer = match result_bv {
108 Some(bv) => apply_option_bitvec(value_buffer, bv),
109 None => value_buffer,
110 };
111
112 Ok(ColumnWithName::new(fragment.clone(), final_buffer))
113}
114
115fn type_error(
116 logical_op: &LogicalOp,
117 fragment: &Fragment,
118 left: &ColumnBuffer,
119 right: &ColumnBuffer,
120) -> Result<ColumnWithName> {
121 let category = if left.is_number() || right.is_number() {
122 OperandCategory::Number
123 } else if left.is_text() || right.is_text() {
124 OperandCategory::Text
125 } else if left.is_temporal() || right.is_temporal() {
126 OperandCategory::Temporal
127 } else if left.is_uuid() || right.is_uuid() {
128 OperandCategory::Uuid
129 } else {
130 unimplemented!("{} {:?} {}", left.get_type(), logical_op, right.get_type());
131 };
132 Err(TypeError::LogicalOperatorNotApplicable {
133 operator: logical_op.clone(),
134 operand_category: category,
135 fragment: fragment.clone(),
136 }
137 .into())
138}
139
140fn compute_kleene_validity(
141 logical_op: &LogicalOp,
142 left_bv: Option<&BitVec>,
143 right_bv: Option<&BitVec>,
144 l_data: &BitVec,
145 r_data: &BitVec,
146 len: usize,
147) -> Option<BitVec> {
148 if left_bv.is_none() && right_bv.is_none() {
149 return None;
150 }
151 let bv = BitVec::from_fn(len, |i| {
152 let l_valid = left_bv.is_none_or(|bv| bv.get(i));
153 let r_valid = right_bv.is_none_or(|bv| bv.get(i));
154 let l_v = l_data.get(i);
155 let r_v = r_data.get(i);
156 let both_valid = l_valid && r_valid;
157 let l_false = l_valid && !l_v;
158 let r_false = r_valid && !r_v;
159 let l_true = l_valid && l_v;
160 let r_true = r_valid && r_v;
161 match logical_op {
162 LogicalOp::And => both_valid || l_false || r_false,
163 LogicalOp::Or => both_valid || l_true || r_true,
164 LogicalOp::Xor => both_valid,
165 LogicalOp::Not => unreachable!("NOT is unary; not handled by execute_logical_op"),
166 }
167 });
168 Some(bv)
169}