1use std::{fmt::Display, result::Result as StdResult, sync::Arc};
5
6use arrow_array::{
7 Array, ArrayRef, ArrowPrimitiveType, BooleanArray, Datum, FixedSizeBinaryArray, PrimitiveArray, Scalar,
8 types::{
9 Decimal128Type, Decimal256Type, Float64Type, Int16Type, Int32Type, Int64Type, UInt16Type, UInt32Type,
10 UInt64Type,
11 },
12};
13use arrow_buffer::i256;
14use arrow_ord::cmp;
15use arrow_schema::{Field, FieldRef};
16use reifydb_core::{
17 error::CoreError,
18 value::column::{
19 arith::{family_digits, is_family, length_mismatch, option::is_all_none},
20 factory::none_typed,
21 nulls::split_nulls,
22 },
23};
24use reifydb_value::{
25 error::Diagnostic,
26 fragment::Fragment,
27 return_error,
28 value::{
29 column_view::{ColumnView, ViewData},
30 constraint::{precision::Precision, scale::Scale},
31 container::{
32 decimal_array::{self, DECIMAL128_MAX_PRECISION, DecimalView},
33 fixed_array,
34 wide_int_array::{WideInt, wide_array, wides},
35 },
36 decimal::unscaled,
37 ordered_f64::OrderedF64,
38 value_type::{ValueType, field::from_field},
39 },
40};
41
42use super::logic::bool_column;
43use crate::Result;
44
45pub trait CompareOp {
46 const EQUALITY: bool = false;
47
48 fn kernel(left: &dyn Datum, right: &dyn Datum) -> Result<BooleanArray>;
49}
50
51pub struct Equal;
52pub struct NotEqual;
53pub struct GreaterThan;
54pub struct GreaterThanEqual;
55pub struct LessThan;
56pub struct LessThanEqual;
57
58impl CompareOp for Equal {
59 const EQUALITY: bool = true;
60
61 fn kernel(left: &dyn Datum, right: &dyn Datum) -> Result<BooleanArray> {
62 arrow_result(cmp::eq(left, right))
63 }
64}
65
66impl CompareOp for NotEqual {
67 const EQUALITY: bool = true;
68
69 fn kernel(left: &dyn Datum, right: &dyn Datum) -> Result<BooleanArray> {
70 arrow_result(cmp::neq(left, right))
71 }
72}
73
74impl CompareOp for GreaterThan {
75 fn kernel(left: &dyn Datum, right: &dyn Datum) -> Result<BooleanArray> {
76 arrow_result(cmp::gt(left, right))
77 }
78}
79
80impl CompareOp for GreaterThanEqual {
81 fn kernel(left: &dyn Datum, right: &dyn Datum) -> Result<BooleanArray> {
82 arrow_result(cmp::gt_eq(left, right))
83 }
84}
85
86impl CompareOp for LessThan {
87 fn kernel(left: &dyn Datum, right: &dyn Datum) -> Result<BooleanArray> {
88 arrow_result(cmp::lt(left, right))
89 }
90}
91
92impl CompareOp for LessThanEqual {
93 fn kernel(left: &dyn Datum, right: &dyn Datum) -> Result<BooleanArray> {
94 arrow_result(cmp::lt_eq(left, right))
95 }
96}
97
98fn arrow_result<E: Display>(result: StdResult<BooleanArray, E>) -> Result<BooleanArray> {
99 Ok(result.map_err(|err| CoreError::FrameError {
100 message: err.to_string(),
101 })?)
102}
103
104fn signed_rank(ty: &ValueType) -> Option<u8> {
105 match ty {
106 ValueType::Int1 => Some(1),
107 ValueType::Int2 => Some(2),
108 ValueType::Int4 => Some(3),
109 ValueType::Int8 => Some(4),
110 ValueType::Int16 => Some(5),
111 _ => None,
112 }
113}
114
115fn unsigned_rank(ty: &ValueType) -> Option<u8> {
116 match ty {
117 ValueType::Uint1 => Some(1),
118 ValueType::Uint2 => Some(2),
119 ValueType::Uint4 => Some(3),
120 ValueType::Uint8 => Some(4),
121 ValueType::Uint16 => Some(5),
122 _ => None,
123 }
124}
125
126fn signed_of_rank(rank: u8) -> ValueType {
127 match rank {
128 1 => ValueType::Int1,
129 2 => ValueType::Int2,
130 3 => ValueType::Int4,
131 4 => ValueType::Int8,
132 5 => ValueType::Int16,
133 _ => ValueType::decimal(Precision::new(39), Scale::new(0)),
134 }
135}
136
137fn unsigned_of_rank(rank: u8) -> ValueType {
138 match rank {
139 1 => ValueType::Uint1,
140 2 => ValueType::Uint2,
141 3 => ValueType::Uint4,
142 4 => ValueType::Uint8,
143 _ => ValueType::Uint16,
144 }
145}
146
147pub(crate) fn compare_target(left: &ValueType, right: &ValueType) -> Option<ValueType> {
148 if is_family(left) || is_family(right) {
149 if left.is_floating_point() || right.is_floating_point() {
150 return (left.is_number() && right.is_number()).then_some(ValueType::Float8);
151 }
152 let ((left_digits, left_scale), (right_digits, right_scale)) =
153 (family_digits(left)?, family_digits(right)?);
154 let scale = left_scale.max(right_scale);
155 let precision = (left_digits.max(right_digits) + scale).min(unscaled::MAX_DIGITS);
156 return Some(ValueType::decimal(Precision::new(precision), Scale::new(scale)));
157 }
158 if left == right {
159 return matches!(
160 left,
161 ValueType::Boolean
162 | ValueType::Float4 | ValueType::Float8
163 | ValueType::Int1 | ValueType::Int2
164 | ValueType::Int4 | ValueType::Int8
165 | ValueType::Int16 | ValueType::Uint1
166 | ValueType::Uint2 | ValueType::Uint4
167 | ValueType::Uint8 | ValueType::Uint16
168 | ValueType::Utf8 | ValueType::Blob
169 | ValueType::Date | ValueType::DateTime
170 | ValueType::Time | ValueType::Duration
171 | ValueType::Uuid4 | ValueType::Uuid7
172 | ValueType::IdentityId | ValueType::DictionaryId
173 )
174 .then(|| left.clone());
175 }
176 if left.is_floating_point() || right.is_floating_point() {
177 return (left.is_number() && right.is_number()).then_some(ValueType::Float8);
178 }
179 match (signed_rank(left), unsigned_rank(left), signed_rank(right), unsigned_rank(right)) {
180 (Some(l), _, Some(r), _) => Some(signed_of_rank(l.max(r))),
181 (_, Some(l), _, Some(r)) => Some(unsigned_of_rank(l.max(r))),
182 (Some(s), _, _, Some(u)) | (_, Some(u), Some(s), _) => Some(signed_of_rank(s.max(u + 1))),
183 _ => None,
184 }
185}
186
187pub(crate) fn field_compare_target(
188 (left, left_type): (&Field, &ValueType),
189 (right, right_type): (&Field, &ValueType),
190 equality: bool,
191) -> Result<Option<ValueType>> {
192 let target = compare_target(left_type, right_type);
193 if target != Some(ValueType::DictionaryId) {
194 return Ok(target);
195 }
196 let dictionary = from_field(left)?.dictionary_id;
197 Ok((equality && dictionary.is_some() && dictionary == from_field(right)?.dictionary_id)
198 .then_some(ValueType::DictionaryId))
199}
200
201fn wide_unary<W: WideInt, T: ArrowPrimitiveType>(
202 array: &FixedSizeBinaryArray,
203 f: impl Fn(W) -> T::Native,
204) -> PrimitiveArray<T> {
205 PrimitiveArray::new(wides::<W>(array).into_iter().map(f).collect(), array.logical_nulls())
206}
207
208fn to_wide<T: ArrowPrimitiveType, W: WideInt>(
209 array: &PrimitiveArray<T>,
210 f: impl Fn(T::Native) -> W,
211) -> FixedSizeBinaryArray {
212 fixed_array::attach_nulls(wide_array(array.values().iter().map(|&v| f(v))), array.logical_nulls())
213}
214
215macro_rules! widen {
216 ($array:expr, $to:ty, |$v:ident| $conv:expr) => {
217 Arc::new($array.unary::<_, $to>(|$v| $conv)) as ArrayRef
218 };
219}
220
221pub(crate) fn cast_to(column: &(FieldRef, ArrayRef), target: &ValueType) -> Result<ArrayRef> {
222 let view = ColumnView::try_from(column)?;
223 if view.get_type().inner_type() == target {
224 return Ok(column.1.clone());
225 }
226 Ok(match (target, &view.data) {
227 (ValueType::Float8, ViewData::Float4(a)) => widen!(a, Float64Type, |v| OrderedF64::canonical(v as f64)),
228 (ValueType::Float8, ViewData::Int1(a)) => widen!(a, Float64Type, |v| v as f64),
229 (ValueType::Float8, ViewData::Int2(a)) => widen!(a, Float64Type, |v| v as f64),
230 (ValueType::Float8, ViewData::Int4(a)) => widen!(a, Float64Type, |v| v as f64),
231 (ValueType::Float8, ViewData::Int8(a)) => widen!(a, Float64Type, |v| v as f64),
232 (ValueType::Float8, ViewData::Int16(a)) => Arc::new(wide_unary::<i128, Float64Type>(a, |v| v as f64)),
233 (ValueType::Float8, ViewData::Uint1(a)) => widen!(a, Float64Type, |v| v as f64),
234 (ValueType::Float8, ViewData::Uint2(a)) => widen!(a, Float64Type, |v| v as f64),
235 (ValueType::Float8, ViewData::Uint4(a)) => widen!(a, Float64Type, |v| v as f64),
236 (ValueType::Float8, ViewData::Uint8(a)) => widen!(a, Float64Type, |v| v as f64),
237 (ValueType::Float8, ViewData::Uint16(a)) => Arc::new(wide_unary::<u128, Float64Type>(a, |v| v as f64)),
238 (ValueType::Int2, ViewData::Int1(a)) => widen!(a, Int16Type, |v| v as i16),
239 (ValueType::Int2, ViewData::Uint1(a)) => widen!(a, Int16Type, |v| v as i16),
240 (ValueType::Int4, ViewData::Int1(a)) => widen!(a, Int32Type, |v| v as i32),
241 (ValueType::Int4, ViewData::Int2(a)) => widen!(a, Int32Type, |v| v as i32),
242 (ValueType::Int4, ViewData::Uint1(a)) => widen!(a, Int32Type, |v| v as i32),
243 (ValueType::Int4, ViewData::Uint2(a)) => widen!(a, Int32Type, |v| v as i32),
244 (ValueType::Int8, ViewData::Int1(a)) => widen!(a, Int64Type, |v| v as i64),
245 (ValueType::Int8, ViewData::Int2(a)) => widen!(a, Int64Type, |v| v as i64),
246 (ValueType::Int8, ViewData::Int4(a)) => widen!(a, Int64Type, |v| v as i64),
247 (ValueType::Int8, ViewData::Uint1(a)) => widen!(a, Int64Type, |v| v as i64),
248 (ValueType::Int8, ViewData::Uint2(a)) => widen!(a, Int64Type, |v| v as i64),
249 (ValueType::Int8, ViewData::Uint4(a)) => widen!(a, Int64Type, |v| v as i64),
250 (ValueType::Uint2, ViewData::Uint1(a)) => widen!(a, UInt16Type, |v| v as u16),
251 (ValueType::Uint4, ViewData::Uint1(a)) => widen!(a, UInt32Type, |v| v as u32),
252 (ValueType::Uint4, ViewData::Uint2(a)) => widen!(a, UInt32Type, |v| v as u32),
253 (ValueType::Uint8, ViewData::Uint1(a)) => widen!(a, UInt64Type, |v| v as u64),
254 (ValueType::Uint8, ViewData::Uint2(a)) => widen!(a, UInt64Type, |v| v as u64),
255 (ValueType::Uint8, ViewData::Uint4(a)) => widen!(a, UInt64Type, |v| v as u64),
256 (ValueType::Int16, ViewData::Int1(a)) => Arc::new(to_wide(a, |v| v as i128)),
257 (ValueType::Int16, ViewData::Int2(a)) => Arc::new(to_wide(a, |v| v as i128)),
258 (ValueType::Int16, ViewData::Int4(a)) => Arc::new(to_wide(a, |v| v as i128)),
259 (ValueType::Int16, ViewData::Int8(a)) => Arc::new(to_wide(a, |v| v as i128)),
260 (ValueType::Int16, ViewData::Uint1(a)) => Arc::new(to_wide(a, |v| v as i128)),
261 (ValueType::Int16, ViewData::Uint2(a)) => Arc::new(to_wide(a, |v| v as i128)),
262 (ValueType::Int16, ViewData::Uint4(a)) => Arc::new(to_wide(a, |v| v as i128)),
263 (ValueType::Int16, ViewData::Uint8(a)) => Arc::new(to_wide(a, |v| v as i128)),
264 (ValueType::Uint16, ViewData::Uint1(a)) => Arc::new(to_wide(a, |v| v as u128)),
265 (ValueType::Uint16, ViewData::Uint2(a)) => Arc::new(to_wide(a, |v| v as u128)),
266 (ValueType::Uint16, ViewData::Uint4(a)) => Arc::new(to_wide(a, |v| v as u128)),
267 (ValueType::Uint16, ViewData::Uint8(a)) => Arc::new(to_wide(a, |v| v as u128)),
268 (ValueType::Float8, ViewData::Decimal(a)) => family_to_float(a),
269 (
270 ValueType::Decimal {
271 precision,
272 scale,
273 },
274 _,
275 ) => family_array(&column.1, &view, *precision, *scale),
276 _ => unreachable!(),
277 })
278}
279
280fn family_to_float(array: &DecimalView) -> ArrayRef {
281 let divisor = 10f64.powi(i32::from(array.scale().value()));
282 match array {
283 DecimalView::Decimal128(a) => widen!(a, Float64Type, |v| v as f64 / divisor),
284 DecimalView::Decimal256(a) => widen!(a, Float64Type, |v| i256_to_f64(v) / divisor),
285 }
286}
287
288fn i256_to_f64(value: i256) -> f64 {
289 match value.to_i128() {
290 Some(narrow) => narrow as f64,
291 None => {
292 let (low, high) = value.to_parts();
293 high as f64 * 2f64.powi(128) + low as f64
294 }
295 }
296}
297
298const BEYOND_FAMILY: i256 = unscaled::MAX.wrapping_add(i256::ONE);
299
300fn upscale_or_beyond(value: i256, by: u8) -> i256 {
301 unscaled::upscale(value, by).unwrap_or(if value.is_negative() {
302 BEYOND_FAMILY.wrapping_neg()
303 } else {
304 BEYOND_FAMILY
305 })
306}
307
308fn family_scale(view: &ColumnView) -> u8 {
309 match &view.data {
310 ViewData::Decimal(a) => a.scale().value(),
311 _ => 0,
312 }
313}
314
315macro_rules! rescale128 {
316 ($array:expr, $factor:expr) => {
317 $array.unary::<_, Decimal128Type>(|v| i128::from(v).wrapping_mul($factor))
318 };
319}
320
321macro_rules! rescale256 {
322 ($array:expr, $by:expr, |$v:ident| $wide:expr) => {
323 $array.unary::<_, Decimal256Type>(|$v| upscale_or_beyond($wide, $by))
324 };
325}
326
327fn family_array(column: &ArrayRef, view: &ColumnView, precision: Precision, scale: Scale) -> ArrayRef {
328 let data_type = decimal_array::data_type(precision, scale);
329 if let ViewData::Decimal(a) = &view.data
330 && a.data_type() == &data_type
331 {
332 return column.clone();
333 }
334 let by = scale.value() - family_scale(view);
335 if precision.value() <= DECIMAL128_MAX_PRECISION {
336 let factor = 10i128.pow(u32::from(by));
337 let array = match &view.data {
338 ViewData::Int1(a) => rescale128!(a, factor),
339 ViewData::Int2(a) => rescale128!(a, factor),
340 ViewData::Int4(a) => rescale128!(a, factor),
341 ViewData::Int8(a) => rescale128!(a, factor),
342 ViewData::Uint1(a) => rescale128!(a, factor),
343 ViewData::Uint2(a) => rescale128!(a, factor),
344 ViewData::Uint4(a) => rescale128!(a, factor),
345 ViewData::Uint8(a) => rescale128!(a, factor),
346 ViewData::Decimal(DecimalView::Decimal128(a)) => rescale128!(a, factor),
347 _ => unreachable!(),
348 };
349 Arc::new(array.with_data_type(data_type))
350 } else {
351 let array = match &view.data {
352 ViewData::Int1(a) => rescale256!(a, by, |v| i256::from_i128(i128::from(v))),
353 ViewData::Int2(a) => rescale256!(a, by, |v| i256::from_i128(i128::from(v))),
354 ViewData::Int4(a) => rescale256!(a, by, |v| i256::from_i128(i128::from(v))),
355 ViewData::Int8(a) => rescale256!(a, by, |v| i256::from_i128(i128::from(v))),
356 ViewData::Int16(a) => {
357 wide_unary::<i128, Decimal256Type>(a, |v| upscale_or_beyond(i256::from_i128(v), by))
358 }
359 ViewData::Uint1(a) => rescale256!(a, by, |v| i256::from_i128(i128::from(v))),
360 ViewData::Uint2(a) => rescale256!(a, by, |v| i256::from_i128(i128::from(v))),
361 ViewData::Uint4(a) => rescale256!(a, by, |v| i256::from_i128(i128::from(v))),
362 ViewData::Uint8(a) => rescale256!(a, by, |v| i256::from_i128(i128::from(v))),
363 ViewData::Uint16(a) => {
364 wide_unary::<u128, Decimal256Type>(a, |v| upscale_or_beyond(i256::from_parts(v, 0), by))
365 }
366 ViewData::Decimal(DecimalView::Decimal128(a)) => {
367 rescale256!(a, by, |v| i256::from_i128(v))
368 }
369 ViewData::Decimal(DecimalView::Decimal256(a)) => rescale256!(a, by, |v| v),
370 _ => unreachable!(),
371 };
372 Arc::new(array.with_data_type(data_type))
373 }
374}
375
376pub fn compare_columns<Op: CompareOp>(
377 left: &(FieldRef, ArrayRef),
378 right: &(FieldRef, ArrayRef),
379 fragment: Fragment,
380 error_fn: impl FnOnce(Fragment, ValueType, ValueType) -> Diagnostic,
381) -> Result<(FieldRef, ArrayRef)> {
382 let len = match (left.1.len(), right.1.len()) {
383 (l, r) if l == r => l,
384 (1, r) => r,
385 (l, 1) => l,
386 (l, r) => return Err(length_mismatch(l, r, &fragment)),
387 };
388 let (left_data, left_nulls) = split_nulls(left.clone())?;
389 let (right_data, right_nulls) = split_nulls(right.clone())?;
390 if ColumnView::try_from(left)?.is_untyped_none() || ColumnView::try_from(right)?.is_untyped_none() {
391 return Ok(none_typed(fragment.text(), ValueType::Boolean, len));
392 }
393 let (left_type, right_type) =
394 (ColumnView::try_from(&left_data)?.get_type(), ColumnView::try_from(&right_data)?.get_type());
395 let Some(target) = field_compare_target((&left.0, &left_type), (&right.0, &right_type), Op::EQUALITY)? else {
396 return_error!(error_fn(fragment, left_type, right_type))
397 };
398 if is_all_none(left_nulls.as_ref()) || is_all_none(right_nulls.as_ref()) {
399 return Ok(none_typed(fragment.text(), ValueType::Boolean, len));
400 }
401 let left_array = cast_to(left, &target)?;
402 let right_array = cast_to(right, &target)?;
403 let result = kernel_compare::<Op>(left_array, right_array)?;
404 Ok(bool_column(fragment.text(), result, left.0.is_nullable() || right.0.is_nullable()))
405}
406
407fn kernel_compare<Op: CompareOp>(left_array: ArrayRef, right_array: ArrayRef) -> Result<BooleanArray> {
408 Ok(match (left_array.len(), right_array.len()) {
409 (1, r) if r != 1 => Op::kernel(&Scalar::new(left_array), &right_array)?,
410 (l, 1) if l != 1 => Op::kernel(&left_array, &Scalar::new(right_array))?,
411 _ => Op::kernel(&left_array, &right_array)?,
412 })
413}
414
415#[cfg(test)]
416mod tests {
417 use arrow_schema::FieldRef;
418 use reifydb_core::value::column::builder::ColumnBuilder;
419 use reifydb_value::value::{dictionary::DictionaryId, value_type::ValueType};
420
421 use super::field_compare_target;
422
423 fn id_field(dictionary: Option<DictionaryId>) -> FieldRef {
424 let mut builder = ColumnBuilder::with_capacity(ValueType::DictionaryId, 0);
425 if let Some(dictionary) = dictionary {
426 builder.set_dictionary_id(dictionary);
427 }
428 builder.finish("c").0
429 }
430
431 fn target(left: Option<DictionaryId>, right: Option<DictionaryId>, equality: bool) -> Option<ValueType> {
432 let (left, right) = (id_field(left), id_field(right));
433 field_compare_target((&left, &ValueType::DictionaryId), (&right, &ValueType::DictionaryId), equality)
434 .unwrap()
435 }
436
437 #[test]
438 fn ids_compare_as_ids_only_for_equality_within_one_dictionary() {
439 let (syms, other) = (DictionaryId(1), DictionaryId(2));
442 assert_eq!(target(Some(syms), Some(syms), true), Some(ValueType::DictionaryId));
443 assert_eq!(target(Some(syms), Some(other), true), None);
444 assert_eq!(target(Some(syms), Some(syms), false), None);
445 assert_eq!(target(Some(syms), None, true), None);
446 assert_eq!(target(None, None, true), None);
447 }
448}