From 245709f4df201811c2bf7459809fb5b8d5c57409 Mon Sep 17 00:00:00 2001 From: Ian Joiner <14581281+iajoiner@users.noreply.github.com> Date: Wed, 6 Nov 2024 22:44:54 -0500 Subject: [PATCH 1/2] refactor: split and rename `column_operation.rs` - split out `ColumnType`-related operations to `column_type_operation.rs` - rename the remaining file to `slice_operation.rs` --- .../base/database/column_type_operation.rs | 755 ++++++++++++++++++ crates/proof-of-sql/src/base/database/mod.rs | 6 +- .../base/database/owned_column_operation.rs | 2 +- ...column_operation.rs => slice_operation.rs} | 755 +----------------- 4 files changed, 768 insertions(+), 750 deletions(-) create mode 100644 crates/proof-of-sql/src/base/database/column_type_operation.rs rename crates/proof-of-sql/src/base/database/{column_operation.rs => slice_operation.rs} (69%) diff --git a/crates/proof-of-sql/src/base/database/column_type_operation.rs b/crates/proof-of-sql/src/base/database/column_type_operation.rs new file mode 100644 index 000000000..21300dfd5 --- /dev/null +++ b/crates/proof-of-sql/src/base/database/column_type_operation.rs @@ -0,0 +1,755 @@ +use super::{ColumnOperationError, ColumnOperationResult}; +use crate::base::{ + database::ColumnType, + math::decimal::{DecimalError, Precision}, +}; +use alloc::{format, string::ToString}; +use proof_of_sql_parser::intermediate_ast::BinaryOperator; +// For decimal type manipulation please refer to +// https://learn.microsoft.com/en-us/sql/t-sql/data-types/precision-scale-and-length-transact-sql?view=sql-server-ver16 + +/// Determine the output type of an add or subtract operation if it is possible +/// to add or subtract the two input types. If the types are not compatible, return +/// an error. +/// +/// # Panics +/// +/// - Panics if `lhs` or `rhs` does not have a precision or scale when they are expected to be numeric types. +/// - Panics if `lhs` or `rhs` is an integer, and `lhs.max_integer_type(&rhs)` returns `None`. +pub fn try_add_subtract_column_types( + lhs: ColumnType, + rhs: ColumnType, + operator: BinaryOperator, +) -> ColumnOperationResult { + if !lhs.is_numeric() || !rhs.is_numeric() { + return Err(ColumnOperationError::BinaryOperationInvalidColumnType { + operator, + left_type: lhs, + right_type: rhs, + }); + } + if lhs.is_integer() && rhs.is_integer() { + // We can unwrap here because we know that both types are integers + return Ok(lhs.max_integer_type(&rhs).unwrap()); + } + if lhs == ColumnType::Scalar || rhs == ColumnType::Scalar { + Ok(ColumnType::Scalar) + } else { + let left_precision_value = + i16::from(lhs.precision_value().expect("Numeric types have precision")); + let right_precision_value = + i16::from(rhs.precision_value().expect("Numeric types have precision")); + let left_scale = lhs.scale().expect("Numeric types have scale"); + let right_scale = rhs.scale().expect("Numeric types have scale"); + let scale = left_scale.max(right_scale); + let precision_value: i16 = i16::from(scale) + + (left_precision_value - i16::from(left_scale)) + .max(right_precision_value - i16::from(right_scale)) + + 1_i16; + let precision = u8::try_from(precision_value) + .map_err(|_| ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { + error: precision_value.to_string(), + }, + }) + .and_then(|p| { + Precision::new(p).map_err(|_| ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { + error: p.to_string(), + }, + }) + })?; + Ok(ColumnType::Decimal75(precision, scale)) + } +} + +/// Determine the output type of a multiplication operation if it is possible +/// to multiply the two input types. If the types are not compatible, return +/// an error. +/// +/// # Panics +/// +/// - Panics if `lhs` or `rhs` does not have a precision or scale when they are expected to be numeric types. +/// - Panics if `lhs` or `rhs` is an integer, and `lhs.max_integer_type(&rhs)` returns `None`. +pub fn try_multiply_column_types( + lhs: ColumnType, + rhs: ColumnType, +) -> ColumnOperationResult { + if !lhs.is_numeric() || !rhs.is_numeric() { + return Err(ColumnOperationError::BinaryOperationInvalidColumnType { + operator: BinaryOperator::Multiply, + left_type: lhs, + right_type: rhs, + }); + } + if lhs.is_integer() && rhs.is_integer() { + // We can unwrap here because we know that both types are integers + return Ok(lhs.max_integer_type(&rhs).unwrap()); + } + if lhs == ColumnType::Scalar || rhs == ColumnType::Scalar { + Ok(ColumnType::Scalar) + } else { + let left_precision_value = lhs.precision_value().expect("Numeric types have precision"); + let right_precision_value = rhs.precision_value().expect("Numeric types have precision"); + let precision_value = left_precision_value + right_precision_value + 1; + let precision = Precision::new(precision_value).map_err(|_| { + ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { + error: format!( + "Required precision {precision_value} is beyond what we can support" + ), + }, + } + })?; + let left_scale = lhs.scale().expect("Numeric types have scale"); + let right_scale = rhs.scale().expect("Numeric types have scale"); + let scale = left_scale.checked_add(right_scale).ok_or( + ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidScale { + scale: (i16::from(left_scale) + i16::from(right_scale)).to_string(), + }, + }, + )?; + Ok(ColumnType::Decimal75(precision, scale)) + } +} + +/// Determine the output type of a division operation if it is possible +/// to multiply the two input types. If the types are not compatible, return +/// an error. +/// +/// # Panics +/// +/// - Panics if `lhs` or `rhs` does not have a precision or scale when they are expected to be numeric types. +/// - Panics if `lhs` or `rhs` is an integer, and `lhs.max_integer_type(&rhs)` returns `None`. +pub fn try_divide_column_types( + lhs: ColumnType, + rhs: ColumnType, +) -> ColumnOperationResult { + if !lhs.is_numeric() + || !rhs.is_numeric() + || lhs == ColumnType::Scalar + || rhs == ColumnType::Scalar + { + return Err(ColumnOperationError::BinaryOperationInvalidColumnType { + operator: BinaryOperator::Division, + left_type: lhs, + right_type: rhs, + }); + } + if lhs.is_integer() && rhs.is_integer() { + // We can unwrap here because we know that both types are integers + return Ok(lhs.max_integer_type(&rhs).unwrap()); + } + let left_precision_value = + i16::from(lhs.precision_value().expect("Numeric types have precision")); + let right_precision_value = + i16::from(rhs.precision_value().expect("Numeric types have precision")); + let left_scale = i16::from(lhs.scale().expect("Numeric types have scale")); + let right_scale = i16::from(rhs.scale().expect("Numeric types have scale")); + let raw_scale = (left_scale + right_precision_value + 1_i16).max(6_i16); + let precision_value: i16 = left_precision_value - left_scale + right_scale + raw_scale; + let scale = + i8::try_from(raw_scale).map_err(|_| ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidScale { + scale: raw_scale.to_string(), + }, + })?; + let precision = u8::try_from(precision_value) + .map_err(|_| ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { + error: precision_value.to_string(), + }, + }) + .and_then(|p| { + Precision::new(p).map_err(|_| ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { + error: p.to_string(), + }, + }) + })?; + Ok(ColumnType::Decimal75(precision, scale)) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn we_can_add_numeric_types() { + // lhs and rhs are integers with the same precision + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::TinyInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::TinyInt; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::SmallInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::SmallInt; + assert_eq!(expected, actual); + + // lhs and rhs are integers with different precision + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::SmallInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::SmallInt; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Int; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Int; + assert_eq!(expected, actual); + + // lhs is an integer and rhs is a scalar + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::Scalar; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Scalar; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Scalar; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Scalar; + assert_eq!(expected, actual); + + // lhs is a decimal with nonnegative scale and rhs is an integer + let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let rhs = ColumnType::TinyInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(11).unwrap(), 2); + assert_eq!(expected, actual); + + let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let rhs = ColumnType::SmallInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(11).unwrap(), 2); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = ColumnType::Decimal75(Precision::new(20).unwrap(), 3); + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(21).unwrap(), 3); + assert_eq!(expected, actual); + + // lhs is an integer and rhs is a decimal with negative scale + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(13).unwrap(), 0); + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(13).unwrap(), 0); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals one of which has negative scale + let lhs = ColumnType::Decimal75(Precision::new(40).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 5); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(59).unwrap(), 5); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals both with negative scale + // and with result having maximum precision + let lhs = ColumnType::Decimal75(Precision::new(74).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), -14); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(75).unwrap(), -13); + assert_eq!(expected, actual); + } + + #[test] + fn we_cannot_add_non_numeric_types() { + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::VarChar; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + } + + #[test] + fn we_cannot_add_some_numeric_types_due_to_decimal_issues() { + let lhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 4); + let rhs = ColumnType::Decimal75(Precision::new(73).unwrap(), 4); + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { .. } + }) + )); + + let lhs = ColumnType::Int; + let rhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 10); + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { .. } + }) + )); + } + + #[test] + fn we_can_subtract_numeric_types() { + // lhs and rhs are integers with the same precision + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::TinyInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::TinyInt; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::SmallInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::SmallInt; + assert_eq!(expected, actual); + + // lhs and rhs are integers with different precision + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::SmallInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::SmallInt; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Int; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Int; + assert_eq!(expected, actual); + + // lhs is an integer and rhs is a scalar + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::Scalar; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Scalar; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Scalar; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Scalar; + assert_eq!(expected, actual); + + // lhs is a decimal and rhs is an integer + let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let rhs = ColumnType::TinyInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(11).unwrap(), 2); + assert_eq!(expected, actual); + + let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let rhs = ColumnType::SmallInt; + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(11).unwrap(), 2); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = ColumnType::Decimal75(Precision::new(20).unwrap(), 3); + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(21).unwrap(), 3); + assert_eq!(expected, actual); + + // lhs is an integer and rhs is a decimal with negative scale + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(13).unwrap(), 0); + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(13).unwrap(), 0); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals one of which has negative scale + let lhs = ColumnType::Decimal75(Precision::new(40).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 5); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(59).unwrap(), 5); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals both with negative scale + // and with result having maximum precision + let lhs = ColumnType::Decimal75(Precision::new(61).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(73).unwrap(), -14); + let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(75).unwrap(), -13); + assert_eq!(expected, actual); + } + + #[test] + fn we_cannot_subtract_non_numeric_types() { + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::VarChar; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + } + + #[test] + fn we_cannot_subtract_some_numeric_types_due_to_decimal_issues() { + let lhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 0); + let rhs = ColumnType::Decimal75(Precision::new(73).unwrap(), 1); + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { .. } + }) + )); + + let lhs = ColumnType::Int128; + let rhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 12); + assert!(matches!( + try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { .. } + }) + )); + } + + #[test] + fn we_can_multiply_numeric_types() { + // lhs and rhs are integers with the same precision + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::TinyInt; + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::TinyInt; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::SmallInt; + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::SmallInt; + assert_eq!(expected, actual); + + // lhs and rhs are integers with different precision + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::SmallInt; + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::SmallInt; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Int; + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Int; + assert_eq!(expected, actual); + + // lhs is an integer and rhs is a scalar + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::Scalar; + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Scalar; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Scalar; + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Scalar; + assert_eq!(expected, actual); + + // lhs is a decimal and rhs is an integer + let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let rhs = ColumnType::TinyInt; + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(14).unwrap(), 2); + assert_eq!(expected, actual); + + let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let rhs = ColumnType::SmallInt; + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(16).unwrap(), 2); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = ColumnType::Decimal75(Precision::new(20).unwrap(), 3); + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(31).unwrap(), 5); + assert_eq!(expected, actual); + + // lhs is an integer and rhs is a decimal with negative scale + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(14).unwrap(), -2); + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(16).unwrap(), -2); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals one of which has negative scale + let lhs = ColumnType::Decimal75(Precision::new(40).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 5); + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(56).unwrap(), -8); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals both with negative scale + // and with result having maximum precision + let lhs = ColumnType::Decimal75(Precision::new(61).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(13).unwrap(), -14); + let actual = try_multiply_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(75).unwrap(), -27); + assert_eq!(expected, actual); + } + + #[test] + fn we_cannot_multiply_non_numeric_types() { + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_multiply_column_types(lhs, rhs), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_multiply_column_types(lhs, rhs), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::VarChar; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_multiply_column_types(lhs, rhs), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + } + + #[test] + fn we_cannot_multiply_some_numeric_types_due_to_decimal_issues() { + // Invalid precision + let lhs = ColumnType::Decimal75(Precision::new(38).unwrap(), 4); + let rhs = ColumnType::Decimal75(Precision::new(37).unwrap(), 4); + assert!(matches!( + try_multiply_column_types(lhs, rhs), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { .. } + }) + )); + + let lhs = ColumnType::Int; + let rhs = ColumnType::Decimal75(Precision::new(65).unwrap(), 0); + assert!(matches!( + try_multiply_column_types(lhs, rhs), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { .. } + }) + )); + + // Invalid scale + let lhs = ColumnType::Decimal75(Precision::new(5).unwrap(), -64_i8); + let rhs = ColumnType::Decimal75(Precision::new(5).unwrap(), -65_i8); + assert!(matches!( + try_multiply_column_types(lhs, rhs), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidScale { .. } + }) + )); + + let lhs = ColumnType::Decimal75(Precision::new(5).unwrap(), 64_i8); + let rhs = ColumnType::Decimal75(Precision::new(5).unwrap(), 64_i8); + assert!(matches!( + try_multiply_column_types(lhs, rhs), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidScale { .. } + }) + )); + } + + #[test] + fn we_can_divide_numeric_types() { + // lhs and rhs are integers with the same precision + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::TinyInt; + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::TinyInt; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::SmallInt; + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::SmallInt; + assert_eq!(expected, actual); + + // lhs and rhs are integers with different precision + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::SmallInt; + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::SmallInt; + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Int; + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Int; + assert_eq!(expected, actual); + + // lhs is a decimal with nonnegative scale and rhs is an integer + let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let rhs = ColumnType::TinyInt; + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(14).unwrap(), 6); + assert_eq!(expected, actual); + + let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let rhs = ColumnType::SmallInt; + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(16).unwrap(), 8); + assert_eq!(expected, actual); + + // lhs is an integer and rhs is a decimal with nonnegative scale + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(16).unwrap(), 11); + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(18).unwrap(), 11); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = ColumnType::Decimal75(Precision::new(20).unwrap(), 3); + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(33).unwrap(), 14); + assert_eq!(expected, actual); + + // lhs is an integer and rhs is a decimal with negative scale + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(12).unwrap(), 11); + assert_eq!(expected, actual); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(14).unwrap(), 11); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals one of which has negative scale + let lhs = ColumnType::Decimal75(Precision::new(40).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 5); + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(64).unwrap(), 6); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals both with negative scale + // and with result having maximum precision + let lhs = ColumnType::Decimal75(Precision::new(70).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(13).unwrap(), -14); + let actual = try_divide_column_types(lhs, rhs).unwrap(); + let expected = ColumnType::Decimal75(Precision::new(75).unwrap(), 6); + assert_eq!(expected, actual); + } + + #[test] + fn we_cannot_divide_non_numeric_or_scalar_types() { + let lhs = ColumnType::TinyInt; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_divide_column_types(lhs, rhs), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::SmallInt; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_divide_column_types(lhs, rhs), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::VarChar; + let rhs = ColumnType::VarChar; + assert!(matches!( + try_divide_column_types(lhs, rhs), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + + let lhs = ColumnType::Scalar; + let rhs = ColumnType::Scalar; + assert!(matches!( + try_divide_column_types(lhs, rhs), + Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) + )); + } + + #[test] + fn we_cannot_divide_some_numeric_types_due_to_decimal_issues() { + // Invalid precision + let lhs = ColumnType::Decimal75(Precision::new(71).unwrap(), -13); + let rhs = ColumnType::Decimal75(Precision::new(13).unwrap(), -14); + assert!(matches!( + try_divide_column_types(lhs, rhs), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { .. } + }) + )); + + let lhs = ColumnType::Int; + let rhs = ColumnType::Decimal75(Precision::new(68).unwrap(), 67); + assert!(matches!( + try_divide_column_types(lhs, rhs), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidPrecision { .. } + }) + )); + + // Invalid scale + let lhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 53_i8); + let rhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 40_i8); + assert!(matches!( + try_divide_column_types(lhs, rhs), + Err(ColumnOperationError::DecimalConversionError { + source: DecimalError::InvalidScale { .. } + }) + )); + } +} diff --git a/crates/proof-of-sql/src/base/database/mod.rs b/crates/proof-of-sql/src/base/database/mod.rs index 822b798ee..2ef59db59 100644 --- a/crates/proof-of-sql/src/base/database/mod.rs +++ b/crates/proof-of-sql/src/base/database/mod.rs @@ -7,8 +7,10 @@ pub use accessor::{CommitmentAccessor, DataAccessor, MetadataAccessor, SchemaAcc mod column; pub use column::{Column, ColumnField, ColumnRef, ColumnType}; -mod column_operation; -pub use column_operation::{ +mod slice_operation; + +mod column_type_operation; +pub use column_type_operation::{ try_add_subtract_column_types, try_divide_column_types, try_multiply_column_types, }; diff --git a/crates/proof-of-sql/src/base/database/owned_column_operation.rs b/crates/proof-of-sql/src/base/database/owned_column_operation.rs index f0aa1e2ce..8aacd4c33 100644 --- a/crates/proof-of-sql/src/base/database/owned_column_operation.rs +++ b/crates/proof-of-sql/src/base/database/owned_column_operation.rs @@ -1,7 +1,7 @@ use super::{ColumnOperationError, ColumnOperationResult}; use crate::base::{ database::{ - column_operation::{ + slice_operation::{ eq_decimal_columns, ge_decimal_columns, le_decimal_columns, slice_and, slice_eq, slice_eq_with_casting, slice_ge, slice_ge_with_casting, slice_le, slice_le_with_casting, slice_not, slice_or, try_add_decimal_columns, try_add_slices, diff --git a/crates/proof-of-sql/src/base/database/column_operation.rs b/crates/proof-of-sql/src/base/database/slice_operation.rs similarity index 69% rename from crates/proof-of-sql/src/base/database/column_operation.rs rename to crates/proof-of-sql/src/base/database/slice_operation.rs index f5893be27..276996e05 100644 --- a/crates/proof-of-sql/src/base/database/column_operation.rs +++ b/crates/proof-of-sql/src/base/database/slice_operation.rs @@ -1,11 +1,16 @@ #![allow(dead_code)] use super::{ColumnOperationError, ColumnOperationResult}; use crate::base::{ - database::ColumnType, - math::decimal::{DecimalError, Precision}, + database::{ + column_type_operation::{ + try_add_subtract_column_types, try_divide_column_types, try_multiply_column_types, + }, + ColumnType, + }, + math::decimal::Precision, scalar::{Scalar, ScalarExt}, }; -use alloc::{format, string::ToString, vec::Vec}; +use alloc::{format, vec::Vec}; use core::{cmp::Ordering, fmt::Debug}; use num_bigint::BigInt; use num_traits::{ @@ -14,172 +19,6 @@ use num_traits::{ }; use proof_of_sql_parser::intermediate_ast::BinaryOperator; -// For decimal type manipulation please refer to -// https://learn.microsoft.com/en-us/sql/t-sql/data-types/precision-scale-and-length-transact-sql?view=sql-server-ver16 - -/// Determine the output type of an add or subtract operation if it is possible -/// to add or subtract the two input types. If the types are not compatible, return -/// an error. -/// -/// # Panics -/// -/// - Panics if `lhs` or `rhs` does not have a precision or scale when they are expected to be numeric types. -/// - Panics if `lhs` or `rhs` is an integer, and `lhs.max_integer_type(&rhs)` returns `None`. -pub fn try_add_subtract_column_types( - lhs: ColumnType, - rhs: ColumnType, - operator: BinaryOperator, -) -> ColumnOperationResult { - if !lhs.is_numeric() || !rhs.is_numeric() { - return Err(ColumnOperationError::BinaryOperationInvalidColumnType { - operator, - left_type: lhs, - right_type: rhs, - }); - } - if lhs.is_integer() && rhs.is_integer() { - // We can unwrap here because we know that both types are integers - return Ok(lhs.max_integer_type(&rhs).unwrap()); - } - if lhs == ColumnType::Scalar || rhs == ColumnType::Scalar { - Ok(ColumnType::Scalar) - } else { - let left_precision_value = - i16::from(lhs.precision_value().expect("Numeric types have precision")); - let right_precision_value = - i16::from(rhs.precision_value().expect("Numeric types have precision")); - let left_scale = lhs.scale().expect("Numeric types have scale"); - let right_scale = rhs.scale().expect("Numeric types have scale"); - let scale = left_scale.max(right_scale); - let precision_value: i16 = i16::from(scale) - + (left_precision_value - i16::from(left_scale)) - .max(right_precision_value - i16::from(right_scale)) - + 1_i16; - let precision = u8::try_from(precision_value) - .map_err(|_| ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { - error: precision_value.to_string(), - }, - }) - .and_then(|p| { - Precision::new(p).map_err(|_| ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { - error: p.to_string(), - }, - }) - })?; - Ok(ColumnType::Decimal75(precision, scale)) - } -} - -/// Determine the output type of a multiplication operation if it is possible -/// to multiply the two input types. If the types are not compatible, return -/// an error. -/// -/// # Panics -/// -/// - Panics if `lhs` or `rhs` does not have a precision or scale when they are expected to be numeric types. -/// - Panics if `lhs` or `rhs` is an integer, and `lhs.max_integer_type(&rhs)` returns `None`. -pub fn try_multiply_column_types( - lhs: ColumnType, - rhs: ColumnType, -) -> ColumnOperationResult { - if !lhs.is_numeric() || !rhs.is_numeric() { - return Err(ColumnOperationError::BinaryOperationInvalidColumnType { - operator: BinaryOperator::Multiply, - left_type: lhs, - right_type: rhs, - }); - } - if lhs.is_integer() && rhs.is_integer() { - // We can unwrap here because we know that both types are integers - return Ok(lhs.max_integer_type(&rhs).unwrap()); - } - if lhs == ColumnType::Scalar || rhs == ColumnType::Scalar { - Ok(ColumnType::Scalar) - } else { - let left_precision_value = lhs.precision_value().expect("Numeric types have precision"); - let right_precision_value = rhs.precision_value().expect("Numeric types have precision"); - let precision_value = left_precision_value + right_precision_value + 1; - let precision = Precision::new(precision_value).map_err(|_| { - ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { - error: format!( - "Required precision {precision_value} is beyond what we can support" - ), - }, - } - })?; - let left_scale = lhs.scale().expect("Numeric types have scale"); - let right_scale = rhs.scale().expect("Numeric types have scale"); - let scale = left_scale.checked_add(right_scale).ok_or( - ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidScale { - scale: (i16::from(left_scale) + i16::from(right_scale)).to_string(), - }, - }, - )?; - Ok(ColumnType::Decimal75(precision, scale)) - } -} - -/// Determine the output type of a division operation if it is possible -/// to multiply the two input types. If the types are not compatible, return -/// an error. -/// -/// # Panics -/// -/// - Panics if `lhs` or `rhs` does not have a precision or scale when they are expected to be numeric types. -/// - Panics if `lhs` or `rhs` is an integer, and `lhs.max_integer_type(&rhs)` returns `None`. -pub fn try_divide_column_types( - lhs: ColumnType, - rhs: ColumnType, -) -> ColumnOperationResult { - if !lhs.is_numeric() - || !rhs.is_numeric() - || lhs == ColumnType::Scalar - || rhs == ColumnType::Scalar - { - return Err(ColumnOperationError::BinaryOperationInvalidColumnType { - operator: BinaryOperator::Division, - left_type: lhs, - right_type: rhs, - }); - } - if lhs.is_integer() && rhs.is_integer() { - // We can unwrap here because we know that both types are integers - return Ok(lhs.max_integer_type(&rhs).unwrap()); - } - let left_precision_value = - i16::from(lhs.precision_value().expect("Numeric types have precision")); - let right_precision_value = - i16::from(rhs.precision_value().expect("Numeric types have precision")); - let left_scale = i16::from(lhs.scale().expect("Numeric types have scale")); - let right_scale = i16::from(rhs.scale().expect("Numeric types have scale")); - let raw_scale = (left_scale + right_precision_value + 1_i16).max(6_i16); - let precision_value: i16 = left_precision_value - left_scale + right_scale + raw_scale; - let scale = - i8::try_from(raw_scale).map_err(|_| ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidScale { - scale: raw_scale.to_string(), - }, - })?; - let precision = u8::try_from(precision_value) - .map_err(|_| ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { - error: precision_value.to_string(), - }, - }) - .and_then(|p| { - Precision::new(p).map_err(|_| ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { - error: p.to_string(), - }, - }) - })?; - Ok(ColumnType::Decimal75(precision, scale)) -} - // Unary operations /// Negate a slice of boolean values. @@ -973,584 +812,6 @@ mod test { use super::*; use crate::base::scalar::test_scalar::TestScalar; - #[test] - fn we_can_add_numeric_types() { - // lhs and rhs are integers with the same precision - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::TinyInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::TinyInt; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::SmallInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::SmallInt; - assert_eq!(expected, actual); - - // lhs and rhs are integers with different precision - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::SmallInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::SmallInt; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Int; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Int; - assert_eq!(expected, actual); - - // lhs is an integer and rhs is a scalar - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::Scalar; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Scalar; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Scalar; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Scalar; - assert_eq!(expected, actual); - - // lhs is a decimal with nonnegative scale and rhs is an integer - let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let rhs = ColumnType::TinyInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(11).unwrap(), 2); - assert_eq!(expected, actual); - - let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let rhs = ColumnType::SmallInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(11).unwrap(), 2); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = ColumnType::Decimal75(Precision::new(20).unwrap(), 3); - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(21).unwrap(), 3); - assert_eq!(expected, actual); - - // lhs is an integer and rhs is a decimal with negative scale - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(13).unwrap(), 0); - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(13).unwrap(), 0); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals one of which has negative scale - let lhs = ColumnType::Decimal75(Precision::new(40).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 5); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(59).unwrap(), 5); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals both with negative scale - // and with result having maximum precision - let lhs = ColumnType::Decimal75(Precision::new(74).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), -14); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(75).unwrap(), -13); - assert_eq!(expected, actual); - } - - #[test] - fn we_cannot_add_non_numeric_types() { - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::VarChar; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - } - - #[test] - fn we_cannot_add_some_numeric_types_due_to_decimal_issues() { - let lhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 4); - let rhs = ColumnType::Decimal75(Precision::new(73).unwrap(), 4); - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { .. } - }) - )); - - let lhs = ColumnType::Int; - let rhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 10); - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Add), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { .. } - }) - )); - } - - #[test] - fn we_can_subtract_numeric_types() { - // lhs and rhs are integers with the same precision - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::TinyInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::TinyInt; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::SmallInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::SmallInt; - assert_eq!(expected, actual); - - // lhs and rhs are integers with different precision - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::SmallInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::SmallInt; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Int; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Int; - assert_eq!(expected, actual); - - // lhs is an integer and rhs is a scalar - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::Scalar; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Scalar; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Scalar; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Scalar; - assert_eq!(expected, actual); - - // lhs is a decimal and rhs is an integer - let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let rhs = ColumnType::TinyInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(11).unwrap(), 2); - assert_eq!(expected, actual); - - let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let rhs = ColumnType::SmallInt; - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(11).unwrap(), 2); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = ColumnType::Decimal75(Precision::new(20).unwrap(), 3); - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(21).unwrap(), 3); - assert_eq!(expected, actual); - - // lhs is an integer and rhs is a decimal with negative scale - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(13).unwrap(), 0); - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(13).unwrap(), 0); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals one of which has negative scale - let lhs = ColumnType::Decimal75(Precision::new(40).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 5); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(59).unwrap(), 5); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals both with negative scale - // and with result having maximum precision - let lhs = ColumnType::Decimal75(Precision::new(61).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(73).unwrap(), -14); - let actual = try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(75).unwrap(), -13); - assert_eq!(expected, actual); - } - - #[test] - fn we_cannot_subtract_non_numeric_types() { - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::VarChar; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - } - - #[test] - fn we_cannot_subtract_some_numeric_types_due_to_decimal_issues() { - let lhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 0); - let rhs = ColumnType::Decimal75(Precision::new(73).unwrap(), 1); - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { .. } - }) - )); - - let lhs = ColumnType::Int128; - let rhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 12); - assert!(matches!( - try_add_subtract_column_types(lhs, rhs, BinaryOperator::Subtract), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { .. } - }) - )); - } - - #[test] - fn we_can_multiply_numeric_types() { - // lhs and rhs are integers with the same precision - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::TinyInt; - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::TinyInt; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::SmallInt; - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::SmallInt; - assert_eq!(expected, actual); - - // lhs and rhs are integers with different precision - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::SmallInt; - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::SmallInt; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Int; - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Int; - assert_eq!(expected, actual); - - // lhs is an integer and rhs is a scalar - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::Scalar; - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Scalar; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Scalar; - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Scalar; - assert_eq!(expected, actual); - - // lhs is a decimal and rhs is an integer - let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let rhs = ColumnType::TinyInt; - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(14).unwrap(), 2); - assert_eq!(expected, actual); - - let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let rhs = ColumnType::SmallInt; - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(16).unwrap(), 2); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = ColumnType::Decimal75(Precision::new(20).unwrap(), 3); - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(31).unwrap(), 5); - assert_eq!(expected, actual); - - // lhs is an integer and rhs is a decimal with negative scale - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(14).unwrap(), -2); - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(16).unwrap(), -2); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals one of which has negative scale - let lhs = ColumnType::Decimal75(Precision::new(40).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 5); - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(56).unwrap(), -8); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals both with negative scale - // and with result having maximum precision - let lhs = ColumnType::Decimal75(Precision::new(61).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(13).unwrap(), -14); - let actual = try_multiply_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(75).unwrap(), -27); - assert_eq!(expected, actual); - } - - #[test] - fn we_cannot_multiply_non_numeric_types() { - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_multiply_column_types(lhs, rhs), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_multiply_column_types(lhs, rhs), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::VarChar; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_multiply_column_types(lhs, rhs), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - } - - #[test] - fn we_cannot_multiply_some_numeric_types_due_to_decimal_issues() { - // Invalid precision - let lhs = ColumnType::Decimal75(Precision::new(38).unwrap(), 4); - let rhs = ColumnType::Decimal75(Precision::new(37).unwrap(), 4); - assert!(matches!( - try_multiply_column_types(lhs, rhs), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { .. } - }) - )); - - let lhs = ColumnType::Int; - let rhs = ColumnType::Decimal75(Precision::new(65).unwrap(), 0); - assert!(matches!( - try_multiply_column_types(lhs, rhs), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { .. } - }) - )); - - // Invalid scale - let lhs = ColumnType::Decimal75(Precision::new(5).unwrap(), -64_i8); - let rhs = ColumnType::Decimal75(Precision::new(5).unwrap(), -65_i8); - assert!(matches!( - try_multiply_column_types(lhs, rhs), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidScale { .. } - }) - )); - - let lhs = ColumnType::Decimal75(Precision::new(5).unwrap(), 64_i8); - let rhs = ColumnType::Decimal75(Precision::new(5).unwrap(), 64_i8); - assert!(matches!( - try_multiply_column_types(lhs, rhs), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidScale { .. } - }) - )); - } - - #[test] - fn we_can_divide_numeric_types() { - // lhs and rhs are integers with the same precision - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::TinyInt; - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::TinyInt; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::SmallInt; - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::SmallInt; - assert_eq!(expected, actual); - - // lhs and rhs are integers with different precision - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::SmallInt; - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::SmallInt; - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Int; - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Int; - assert_eq!(expected, actual); - - // lhs is a decimal with nonnegative scale and rhs is an integer - let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let rhs = ColumnType::TinyInt; - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(14).unwrap(), 6); - assert_eq!(expected, actual); - - let lhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let rhs = ColumnType::SmallInt; - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(16).unwrap(), 8); - assert_eq!(expected, actual); - - // lhs is an integer and rhs is a decimal with nonnegative scale - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(16).unwrap(), 11); - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(18).unwrap(), 11); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = ColumnType::Decimal75(Precision::new(20).unwrap(), 3); - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(33).unwrap(), 14); - assert_eq!(expected, actual); - - // lhs is an integer and rhs is a decimal with negative scale - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(12).unwrap(), 11); - assert_eq!(expected, actual); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(14).unwrap(), 11); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals one of which has negative scale - let lhs = ColumnType::Decimal75(Precision::new(40).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 5); - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(64).unwrap(), 6); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals both with negative scale - // and with result having maximum precision - let lhs = ColumnType::Decimal75(Precision::new(70).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(13).unwrap(), -14); - let actual = try_divide_column_types(lhs, rhs).unwrap(); - let expected = ColumnType::Decimal75(Precision::new(75).unwrap(), 6); - assert_eq!(expected, actual); - } - - #[test] - fn we_cannot_divide_non_numeric_or_scalar_types() { - let lhs = ColumnType::TinyInt; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_divide_column_types(lhs, rhs), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::SmallInt; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_divide_column_types(lhs, rhs), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::VarChar; - let rhs = ColumnType::VarChar; - assert!(matches!( - try_divide_column_types(lhs, rhs), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - - let lhs = ColumnType::Scalar; - let rhs = ColumnType::Scalar; - assert!(matches!( - try_divide_column_types(lhs, rhs), - Err(ColumnOperationError::BinaryOperationInvalidColumnType { .. }) - )); - } - - #[test] - fn we_cannot_divide_some_numeric_types_due_to_decimal_issues() { - // Invalid precision - let lhs = ColumnType::Decimal75(Precision::new(71).unwrap(), -13); - let rhs = ColumnType::Decimal75(Precision::new(13).unwrap(), -14); - assert!(matches!( - try_divide_column_types(lhs, rhs), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { .. } - }) - )); - - let lhs = ColumnType::Int; - let rhs = ColumnType::Decimal75(Precision::new(68).unwrap(), 67); - assert!(matches!( - try_divide_column_types(lhs, rhs), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidPrecision { .. } - }) - )); - - // Invalid scale - let lhs = ColumnType::Decimal75(Precision::new(15).unwrap(), 53_i8); - let rhs = ColumnType::Decimal75(Precision::new(75).unwrap(), 40_i8); - assert!(matches!( - try_divide_column_types(lhs, rhs), - Err(ColumnOperationError::DecimalConversionError { - source: DecimalError::InvalidScale { .. } - }) - )); - } - // NOT #[test] fn we_can_negate_boolean_slices() { From 4f4d1469462739539821de6fbcb72b1d706459ca Mon Sep 17 00:00:00 2001 From: Ian Joiner <14581281+iajoiner@users.noreply.github.com> Date: Wed, 6 Nov 2024 23:30:05 -0500 Subject: [PATCH 2/2] refactor: split out decimal related code from `slice_operation.rs` to `slice_decimal_operation.rs` --- crates/proof-of-sql/src/base/database/mod.rs | 2 + .../base/database/owned_column_operation.rs | 17 +- .../base/database/slice_decimal_operation.rs | 1319 ++++++++++++++++ .../src/base/database/slice_operation.rs | 1321 +---------------- 4 files changed, 1332 insertions(+), 1327 deletions(-) create mode 100644 crates/proof-of-sql/src/base/database/slice_decimal_operation.rs diff --git a/crates/proof-of-sql/src/base/database/mod.rs b/crates/proof-of-sql/src/base/database/mod.rs index 2ef59db59..756e5301f 100644 --- a/crates/proof-of-sql/src/base/database/mod.rs +++ b/crates/proof-of-sql/src/base/database/mod.rs @@ -9,6 +9,8 @@ pub use column::{Column, ColumnField, ColumnRef, ColumnType}; mod slice_operation; +mod slice_decimal_operation; + mod column_type_operation; pub use column_type_operation::{ try_add_subtract_column_types, try_divide_column_types, try_multiply_column_types, diff --git a/crates/proof-of-sql/src/base/database/owned_column_operation.rs b/crates/proof-of-sql/src/base/database/owned_column_operation.rs index 8aacd4c33..7b021356c 100644 --- a/crates/proof-of-sql/src/base/database/owned_column_operation.rs +++ b/crates/proof-of-sql/src/base/database/owned_column_operation.rs @@ -1,15 +1,16 @@ use super::{ColumnOperationError, ColumnOperationResult}; use crate::base::{ database::{ + slice_decimal_operation::{ + eq_decimal_columns, ge_decimal_columns, le_decimal_columns, try_add_decimal_columns, + try_divide_decimal_columns, try_multiply_decimal_columns, try_subtract_decimal_columns, + }, slice_operation::{ - eq_decimal_columns, ge_decimal_columns, le_decimal_columns, slice_and, slice_eq, - slice_eq_with_casting, slice_ge, slice_ge_with_casting, slice_le, - slice_le_with_casting, slice_not, slice_or, try_add_decimal_columns, try_add_slices, - try_add_slices_with_casting, try_divide_decimal_columns, try_divide_slices, - try_divide_slices_left_upcast, try_divide_slices_right_upcast, - try_multiply_decimal_columns, try_multiply_slices, try_multiply_slices_with_casting, - try_subtract_decimal_columns, try_subtract_slices, try_subtract_slices_left_upcast, - try_subtract_slices_right_upcast, + slice_and, slice_eq, slice_eq_with_casting, slice_ge, slice_ge_with_casting, slice_le, + slice_le_with_casting, slice_not, slice_or, try_add_slices, + try_add_slices_with_casting, try_divide_slices, try_divide_slices_left_upcast, + try_divide_slices_right_upcast, try_multiply_slices, try_multiply_slices_with_casting, + try_subtract_slices, try_subtract_slices_left_upcast, try_subtract_slices_right_upcast, }, OwnedColumn, }, diff --git a/crates/proof-of-sql/src/base/database/slice_decimal_operation.rs b/crates/proof-of-sql/src/base/database/slice_decimal_operation.rs new file mode 100644 index 000000000..d18c92c23 --- /dev/null +++ b/crates/proof-of-sql/src/base/database/slice_decimal_operation.rs @@ -0,0 +1,1319 @@ +use super::{ColumnOperationError, ColumnOperationResult}; +use crate::base::{ + database::{ + column_type_operation::{ + try_add_subtract_column_types, try_divide_column_types, try_multiply_column_types, + }, + ColumnType, + }, + math::decimal::Precision, + scalar::{Scalar, ScalarExt}, +}; +use alloc::vec::Vec; +use core::{cmp::Ordering, fmt::Debug}; +use num_bigint::BigInt; +use num_traits::Zero; +use proof_of_sql_parser::intermediate_ast::BinaryOperator; +/// Check whether a numerical slice is equal to a decimal one. +/// +/// Note that we do not check for length equality here. +/// # Panics +/// This function requires that `lhs` and `rhs` have the same length. +/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. +pub(super) fn eq_decimal_columns( + lhs: &[T], + rhs: &[S], + left_column_type: ColumnType, + right_column_type: ColumnType, +) -> Vec +where + S: Scalar, + T: Copy + Debug + PartialEq + Zero + Into, +{ + let lhs_scale = left_column_type.scale().expect("Numeric types have scale"); + let rhs_scale = right_column_type.scale().expect("Decimal types have scale"); + let max_scale = lhs_scale.max(rhs_scale); + // At most one of the scales is non-zero + if lhs_scale < max_scale { + // If scale difference is above max decimal precision values + // are equal if they are both zero and unequal otherwise + let upscale = max_scale - lhs_scale; + if i8::try_from( + right_column_type + .precision_value() + .expect("Decimal types have scale"), + ) + .is_ok_and(|precision| upscale > precision) + { + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { l.is_zero() && *r == S::ZERO }) + .collect::>() + } else { + let upscale_factor = + S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { Into::::into(*l) * upscale_factor == *r }) + .collect::>() + } + } else if rhs_scale < max_scale { + let upscale = max_scale - rhs_scale; + if i8::try_from( + left_column_type + .precision_value() + .expect("Numeric types have scale"), + ) + .is_ok_and(|precision| upscale > precision) + { + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { l.is_zero() && *r == S::ZERO }) + .collect::>() + } else { + let upscale_factor = + S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { Into::::into(*l) == *r * upscale_factor }) + .collect::>() + } + } else { + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { Into::::into(*l) == *r }) + .collect::>() + } +} + +/// Check whether a numerical slice is less than or equal to a decimal one. +/// +/// Note that we do not check for length equality here. +/// # Panics +/// This function requires that `lhs` and `rhs` have the same length. +/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. +pub(super) fn le_decimal_columns( + lhs: &[T], + rhs: &[S], + left_column_type: ColumnType, + right_column_type: ColumnType, +) -> Vec +where + S: Scalar, + T: Copy + Debug + Ord + Zero + Into, +{ + let lhs_scale = left_column_type.scale().expect("Numeric types have scale"); + let rhs_scale = right_column_type.scale().expect("Decimal types have scale"); + let max_scale = lhs_scale.max(rhs_scale); + // At most one of the scales is non-zero + if lhs_scale < max_scale { + // If scale difference is above max decimal precision the upscaled + // always have larger absolute value than the other one as long as it is nonzero + // Hence a (extremely upscaled) <= b if and only if a < 0 or (a == 0 and b >= 0) + let upscale = max_scale - lhs_scale; + if i8::try_from( + right_column_type + .precision_value() + .expect("Decimal types have scale"), + ) + .is_ok_and(|precision| upscale > precision) + { + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { + Into::::into(*l).signed_cmp(&S::ZERO) == Ordering::Less + || (l.is_zero() && r.signed_cmp(&S::ZERO) != Ordering::Less) + }) + .collect::>() + } else { + let upscale_factor = + S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { + (Into::::into(*l) * upscale_factor).signed_cmp(r) != Ordering::Greater + }) + .collect::>() + } + } else if rhs_scale < max_scale { + let upscale = max_scale - rhs_scale; + if i8::try_from( + left_column_type + .precision_value() + .expect("Numeric types have scale"), + ) + .is_ok_and(|precision| upscale > precision) + { + // Similarly with extreme scaling we have + // a <= (extremely upscaled) b if and only if a < 0 or (a == 0 and b >= 0) + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { + (Into::::into(*l).signed_cmp(&S::ZERO) != Ordering::Greater && *r == S::ZERO) + || r.signed_cmp(&S::ZERO) == Ordering::Greater + }) + .collect::>() + } else { + let upscale_factor = + S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { + Into::::into(*l).signed_cmp(&(*r * upscale_factor)) != Ordering::Greater + }) + .collect::>() + } + } else { + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { Into::::into(*l).signed_cmp(r) != Ordering::Greater }) + .collect::>() + } +} + +/// Check whether a numerical slice is greater than or equal to a decimal one. +/// +/// Note that we do not check for length equality here. +/// # Panics +/// This function requires that `lhs` and `rhs` have the same length. +/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. +pub(super) fn ge_decimal_columns( + lhs: &[T], + rhs: &[S], + left_column_type: ColumnType, + right_column_type: ColumnType, +) -> Vec +where + S: Scalar, + T: Copy + Debug + PartialEq + Zero + Into, +{ + let lhs_scale = left_column_type.scale().expect("Numeric types have scale"); + let rhs_scale = right_column_type.scale().expect("Decimal types have scale"); + let max_scale = lhs_scale.max(rhs_scale); + // At most one of the scales is non-zero + if lhs_scale < max_scale { + // If scale difference is above max decimal precision the upscaled + // always have larger absolute value than the other one as long as it is nonzero + // Hence a (extremely upscaled) >= b if and only if a > 0 or (a == 0 and b <= 0) + let upscale = max_scale - lhs_scale; + if i8::try_from( + right_column_type + .precision_value() + .expect("Decimal types have scale"), + ) + .is_ok_and(|precision| upscale > precision) + { + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { + Into::::into(*l).signed_cmp(&S::ZERO) == Ordering::Greater + || (l.is_zero() && r.signed_cmp(&S::ZERO) != Ordering::Greater) + }) + .collect::>() + } else { + let upscale_factor = + S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { + (Into::::into(*l) * upscale_factor).signed_cmp(r) != Ordering::Less + }) + .collect::>() + } + } else if rhs_scale < max_scale { + let upscale = max_scale - rhs_scale; + if i8::try_from( + left_column_type + .precision_value() + .expect("Numeric types have scale"), + ) + .is_ok_and(|precision| upscale > precision) + { + // Similarly with extreme scaling we have + // a >= (extremely upscaled) b if and only if b < 0 or (a >= 0 and b == 0) + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { + (Into::::into(*l).signed_cmp(&S::ZERO) != Ordering::Less && *r == S::ZERO) + || r.signed_cmp(&S::ZERO) == Ordering::Less + }) + .collect::>() + } else { + let upscale_factor = + S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { + Into::::into(*l).signed_cmp(&(*r * upscale_factor)) != Ordering::Less + }) + .collect::>() + } + } else { + lhs.iter() + .zip(rhs.iter()) + .map(|(l, r)| -> bool { Into::::into(*l).signed_cmp(r) != Ordering::Less }) + .collect::>() + } +} + +/// Add two numerical slices as decimals. +/// +/// We do not check for length equality here +/// # Panics +/// This function requires that `lhs` and `rhs` have the same length. +/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. +pub(super) fn try_add_decimal_columns( + lhs: &[T0], + rhs: &[T1], + left_column_type: ColumnType, + right_column_type: ColumnType, +) -> ColumnOperationResult<(Precision, i8, Vec)> +where + S: Scalar + core::convert::From + core::convert::From, + T0: Copy, + T1: Copy, +{ + let new_column_type = + try_add_subtract_column_types(left_column_type, right_column_type, BinaryOperator::Add)?; + let new_precision_value = new_column_type + .precision_value() + .expect("numeric columns have precision"); + let new_scale = new_column_type.scale().expect("numeric columns have scale"); + let left_upscale = new_scale + - left_column_type + .scale() + .expect("numeric columns have scale"); + let right_upscale = new_scale + - right_column_type + .scale() + .expect("numeric columns have scale"); + // One of left_scale and right_scale is 0 so we can avoid scaling when unnecessary + let scalars: Vec = if left_upscale > 0 { + let upscale_factor = + S::pow10(u8::try_from(left_upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs) + .map(|(l, r)| S::from(*l) * upscale_factor + S::from(*r)) + .collect() + } else if right_upscale > 0 { + let upscale_factor = + S::pow10(u8::try_from(right_upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs) + .map(|(l, r)| S::from(*l) + upscale_factor * S::from(*r)) + .collect() + } else { + lhs.iter() + .zip(rhs) + .map(|(l, r)| S::from(*l) + S::from(*r)) + .collect() + }; + Ok(( + Precision::new(new_precision_value).expect("Precision value is valid"), + new_scale, + scalars, + )) +} + +/// Subtract one numerical slice from another as decimals. +/// +/// We do not check for length equality here +/// # Panics +/// This function requires that `lhs` and `rhs` have the same length. +/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. +pub(super) fn try_subtract_decimal_columns( + lhs: &[T0], + rhs: &[T1], + left_column_type: ColumnType, + right_column_type: ColumnType, +) -> ColumnOperationResult<(Precision, i8, Vec)> +where + S: Scalar + core::convert::From + core::convert::From, + T0: Copy, + T1: Copy, +{ + let new_column_type = try_add_subtract_column_types( + left_column_type, + right_column_type, + BinaryOperator::Subtract, + )?; + let new_precision_value = new_column_type + .precision_value() + .expect("numeric columns have precision"); + let new_scale = new_column_type.scale().expect("numeric columns have scale"); + let left_upscale = new_scale + - left_column_type + .scale() + .expect("numeric columns have scale"); + let right_upscale = new_scale + - right_column_type + .scale() + .expect("numeric columns have scale"); + // One of left_scale and right_scale is 0 so we can avoid scaling when unnecessary + let scalars: Vec = if left_upscale > 0 { + let upscale_factor = + S::pow10(u8::try_from(left_upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs) + .map(|(l, r)| S::from(*l) * upscale_factor - S::from(*r)) + .collect() + } else if right_upscale > 0 { + let upscale_factor = + S::pow10(u8::try_from(right_upscale).expect("Upscale factor is nonnegative")); + lhs.iter() + .zip(rhs) + .map(|(l, r)| S::from(*l) - upscale_factor * S::from(*r)) + .collect() + } else { + lhs.iter() + .zip(rhs) + .map(|(l, r)| S::from(*l) - S::from(*r)) + .collect() + }; + Ok(( + Precision::new(new_precision_value).expect("Precision value is valid"), + new_scale, + scalars, + )) +} + +/// Multiply two numerical slices as decimals. +/// +/// We do not check for length equality here +/// # Panics +/// This function requires that `lhs` and `rhs` have the same length. +/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. +pub(super) fn try_multiply_decimal_columns( + lhs: &[T0], + rhs: &[T1], + left_column_type: ColumnType, + right_column_type: ColumnType, +) -> ColumnOperationResult<(Precision, i8, Vec)> +where + S: Scalar + core::convert::From + core::convert::From, + T0: Copy, + T1: Copy, +{ + let new_column_type = try_multiply_column_types(left_column_type, right_column_type)?; + let new_precision_value = new_column_type + .precision_value() + .expect("numeric columns have precision"); + let new_scale = new_column_type.scale().expect("numeric columns have scale"); + let scalars: Vec = lhs + .iter() + .zip(rhs) + .map(|(l, r)| S::from(*l) * S::from(*r)) + .collect(); + Ok(( + Precision::new(new_precision_value).expect("Precision value is valid"), + new_scale, + scalars, + )) +} + +/// Divide an owned column by another. +/// +/// Notes: +/// 1. We do not check for length equality here. +/// 2. We use floor division for rounding. +/// 3. If division by zero occurs, we return an error. +/// 4. Precision and scale follow T-SQL rules. That is, +/// - `new_scale = max(6, right_precision + left_scale + 1)` +/// - `new_precision = left_precision - left_scale + right_scale + new_scale` +#[allow(clippy::missing_panics_doc)] +pub(crate) fn try_divide_decimal_columns( + lhs: &[T0], + rhs: &[T1], + left_column_type: ColumnType, + right_column_type: ColumnType, +) -> ColumnOperationResult<(Precision, i8, Vec)> +where + S: Scalar, + T0: Copy + Debug + Into, + T1: Copy + Debug + Into, +{ + let new_column_type = try_divide_column_types(left_column_type, right_column_type)?; + let new_precision_value = new_column_type + .precision_value() + .expect("numeric columns have precision"); + let new_scale = new_column_type.scale().expect("numeric columns have scale"); + let lhs_scale = left_column_type + .scale() + .expect("numeric columns have scale"); + let rhs_scale = right_column_type + .scale() + .expect("numeric columns have scale"); + let applied_scale = rhs_scale - lhs_scale + new_scale; + let applied_scale_factor = BigInt::from(10).pow(u32::from(applied_scale.unsigned_abs())); + let result: Vec = lhs + .iter() + .zip(rhs) + .map(|(l, r)| -> ColumnOperationResult { + let lhs_bigint = Into::::into(*l); + let rhs_bigint = Into::::into(*r); + if rhs_bigint.is_zero() { + return Err(ColumnOperationError::DivisionByZero); + } + let new_bigint = if applied_scale >= 0 { + lhs_bigint * &applied_scale_factor / rhs_bigint + } else { + lhs_bigint / rhs_bigint / &applied_scale_factor + }; + Ok(S::try_from(new_bigint).expect("Division result should fit into scalar")) + }) + .collect::>>()?; + Ok(( + Precision::new(new_precision_value).expect("Precision value is valid"), + new_scale, + result, + )) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::base::scalar::test_scalar::TestScalar; + + #[test] + fn we_can_eq_decimal_columns() { + // lhs is integer and rhs is decimal with nonnegative scale + let lhs = [1_i8, -2, 3]; + let rhs = [100_i8, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::TinyInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, false, false]; + assert_eq!(expected, actual); + + let lhs = [1_i16, -2, 3]; + let rhs = [100_i16, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::SmallInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, false, false]; + assert_eq!(expected, actual); + + // lhs is integer and rhs is decimal with negative scale + let lhs = [400_i64, -82, -200]; + let rhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::BigInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, false, true]; + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = [4_i16, -80, 230] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -8, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, true]; + assert_eq!(expected, actual); + + // lhs is decimal with negative scale and rhs is decimal with nonnegative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, 150_000, -20000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); + let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, true]; + assert_eq!(expected, actual); + + // lhs is decimal with nonnegative scale and rhs is decimal with negative scale + let lhs = [71_i64, 150_000, -20000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, true]; + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with negative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, 150_000, -20000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), -46); + let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, true]; + assert_eq!(expected, actual); + + // lhs and rhs are decimals with extreme differences in scale + let lhs = [4_i16, 0, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, 0, -20000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 26); + let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, false]; + assert_eq!(expected, actual); + } + + #[test] + fn we_can_le_decimal_columns() { + // lhs is integer and rhs is decimal with nonnegative scale + let lhs = [1_i8, -2, 3]; + let rhs = [100_i8, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::TinyInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, true, false]; + assert_eq!(expected, actual); + + let lhs = [1_i16, -2, 3]; + let rhs = [100_i16, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::SmallInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, true, false]; + assert_eq!(expected, actual); + + // lhs is integer and rhs is decimal with negative scale + let lhs = [400_i64, -82, -199]; + let rhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::BigInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, true, false]; + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = [4_i16, -80, 230] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -8, 22] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, true, false]; + assert_eq!(expected, actual); + + // lhs is decimal with negative scale and rhs is decimal with nonnegative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, 150_000, -30000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); + let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, false]; + assert_eq!(expected, actual); + + // lhs is decimal with nonnegative scale and rhs is decimal with negative scale + let lhs = [71_i64, 150_000, -19000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, true, false]; + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with negative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71000_i64, 150_000, -21000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), -46); + let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, true, false]; + assert_eq!(expected, actual); + + // lhs and rhs are decimals with extreme differences in scale + let lhs = [1_i16, 1, 1, 0, 0, 0, -1, -1, -1] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [1_i64, 0, -1, 1, 0, -1, 1, 0, -1] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 26); + let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, false, false, true, true, false, true, true, true]; + assert_eq!(expected, actual); + } + + #[test] + fn we_can_ge_decimal_columns() { + // lhs is integer and rhs is decimal with nonnegative scale + let lhs = [1_i8, -2, 3]; + let rhs = [100_i8, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::TinyInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, false, true]; + assert_eq!(expected, actual); + + let lhs = [1_i16, -2, 3]; + let rhs = [100_i16, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::SmallInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, false, true]; + assert_eq!(expected, actual); + + // lhs is integer and rhs is decimal with negative scale + let lhs = [400_i64, -82, 199]; + let rhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::BigInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, false, true]; + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = [4_i16, -80, 230] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -8, -22] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, true]; + assert_eq!(expected, actual); + + // lhs is decimal with negative scale and rhs is decimal with nonnegative scale + let lhs = [-4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, 150_000, -30000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); + let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, true]; + assert_eq!(expected, actual); + + // lhs is decimal with nonnegative scale and rhs is decimal with negative scale + let lhs = [71_i64, 150_000, -19000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, true]; + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with negative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71000_i64, 150_000, -21000] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), -46); + let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![false, true, true]; + assert_eq!(expected, actual); + + // lhs and rhs are decimals with extreme differences in scale + let lhs = [1_i16, 1, 1, 0, 0, 0, -1, -1, -1] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [1_i64, 0, -1, 1, 0, -1, 1, 0, -1] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 26); + let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); + let expected = vec![true, true, true, false, true, true, false, false, false]; + assert_eq!(expected, actual); + } + + #[allow(clippy::too_many_lines)] + #[test] + fn we_can_try_add_decimal_columns() { + // lhs is integer and rhs is decimal with nonnegative scale + let lhs = [1_i8, -2, 3]; + let rhs = [4_i8, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::TinyInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(104), + TestScalar::from(-195), + TestScalar::from(298), + ]; + let expected = (Precision::new(11).unwrap(), 2, expected_scalars); + assert_eq!(expected, actual); + + let lhs = [1_i16, -2, 3]; + let rhs = [4_i16, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::SmallInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(104), + TestScalar::from(-195), + TestScalar::from(298), + ]; + let expected = (Precision::new(11).unwrap(), 2, expected_scalars); + assert_eq!(expected, actual); + + // lhs is decimal with negative scale and rhs is integer + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23]; + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let right_column_type = ColumnType::BigInt; + let actual: (Precision, i8, Vec) = + try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(471), + TestScalar::from(1418), + TestScalar::from(-177), + ]; + let expected = (Precision::new(20).unwrap(), 0, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(12).unwrap(), 2); + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); + let actual: (Precision, i8, Vec) = + try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(111), + TestScalar::from(68), + TestScalar::from(3), + ]; + let expected = (Precision::new(14).unwrap(), 3, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals one of which has negative scale + // and with result having maximum precision + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(69).unwrap(), -2); + let right_column_type = ColumnType::Decimal75(Precision::new(50).unwrap(), 3); + let actual: (Precision, i8, Vec) = + try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(400_071), + TestScalar::from(1_499_918), + TestScalar::from(-199_977), + ]; + let expected = (Precision::new(75).unwrap(), 3, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with negative scale + // and with result having maximum precision and minimum scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(74).unwrap(), -128); + let right_column_type = ColumnType::Decimal75(Precision::new(74).unwrap(), -128); + let actual: (Precision, i8, Vec) = + try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(75), + TestScalar::from(-67), + TestScalar::from(21), + ]; + let expected = (Precision::new(75).unwrap(), -128, expected_scalars); + assert_eq!(expected, actual); + } + + #[allow(clippy::too_many_lines)] + #[test] + fn we_can_try_subtract_decimal_columns() { + // lhs is integer and rhs is decimal with nonnegative scale + let lhs = [1_i8, -2, 3]; + let rhs = [4_i8, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::TinyInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(96), + TestScalar::from(-205), + TestScalar::from(302), + ]; + let expected = (Precision::new(11).unwrap(), 2, expected_scalars); + assert_eq!(expected, actual); + + let lhs = [1_i16, -2, 3]; + let rhs = [4_i16, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::SmallInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(96), + TestScalar::from(-205), + TestScalar::from(302), + ]; + let expected = (Precision::new(11).unwrap(), 2, expected_scalars); + assert_eq!(expected, actual); + + // lhs is decimal with negative scale and rhs is integer + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23]; + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let right_column_type = ColumnType::BigInt; + let actual: (Precision, i8, Vec) = + try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(329), + TestScalar::from(1582), + TestScalar::from(-223), + ]; + let expected = (Precision::new(20).unwrap(), 0, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(12).unwrap(), 2); + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); + let actual: (Precision, i8, Vec) = + try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(-31), + TestScalar::from(232), + TestScalar::from(-43), + ]; + let expected = (Precision::new(14).unwrap(), 3, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals one of which has negative scale + // and with result having maximum precision + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(69).unwrap(), -2); + let right_column_type = ColumnType::Decimal75(Precision::new(50).unwrap(), 3); + let actual: (Precision, i8, Vec) = + try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(399_929), + TestScalar::from(1_500_082), + TestScalar::from(-200_023), + ]; + let expected = (Precision::new(75).unwrap(), 3, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with negative scale + // and with result having maximum precision and minimum scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(74).unwrap(), -128); + let right_column_type = ColumnType::Decimal75(Precision::new(74).unwrap(), -128); + let actual: (Precision, i8, Vec) = + try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(-67), + TestScalar::from(97), + TestScalar::from(-25), + ]; + let expected = (Precision::new(75).unwrap(), -128, expected_scalars); + assert_eq!(expected, actual); + } + + #[allow(clippy::too_many_lines)] + #[test] + fn we_can_try_multiply_decimal_columns() { + // lhs is integer and rhs is decimal with nonnegative scale + let lhs = [1_i8, -2, 3]; + let rhs = [4_i8, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::TinyInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(4), + TestScalar::from(-10), + TestScalar::from(-6), + ]; + let expected = (Precision::new(14).unwrap(), 2, expected_scalars); + assert_eq!(expected, actual); + + let lhs = [1_i16, -2, 3]; + let rhs = [4_i16, 5, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::SmallInt; + let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(4), + TestScalar::from(-10), + TestScalar::from(-6), + ]; + let expected = (Precision::new(16).unwrap(), 2, expected_scalars); + assert_eq!(expected, actual); + + // lhs is decimal with negative scale and rhs is integer + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23]; + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let right_column_type = ColumnType::BigInt; + let actual: (Precision, i8, Vec) = + try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(284), + TestScalar::from(-1230), + TestScalar::from(-46), + ]; + let expected = (Precision::new(30).unwrap(), -2, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + // and with result having maximum precision and maximum scale + let lhs = [4_i16, 25, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(42).unwrap(), 72); + let right_column_type = ColumnType::Decimal75(Precision::new(32).unwrap(), 55); + let actual: (Precision, i8, Vec) = + try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(284), + TestScalar::from(-2050), + TestScalar::from(-46), + ]; + let expected = (Precision::new(75).unwrap(), 127, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals one of which has negative scale + // and with result having maximum precision + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(69).unwrap(), -2); + let right_column_type = ColumnType::Decimal75(Precision::new(5).unwrap(), 3); + let actual: (Precision, i8, Vec) = + try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(284), + TestScalar::from(-1230), + TestScalar::from(-46), + ]; + let expected = (Precision::new(75).unwrap(), 1, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with negative scale + // and with result having maximum precision and minimum scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(34).unwrap(), -64); + let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), -64); + let actual: (Precision, i8, Vec) = + try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(284), + TestScalar::from(-1230), + TestScalar::from(-46), + ]; + let expected = (Precision::new(75).unwrap(), -128, expected_scalars); + assert_eq!(expected, actual); + } + + #[allow(clippy::too_many_lines)] + #[test] + fn we_can_try_divide_decimal_columns() { + // lhs is integer and rhs is decimal with nonnegative scale + let lhs = [0_i8, 2, 3]; + let rhs = [4_i8, 5, 2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::TinyInt; + let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(0_i64), + TestScalar::from(40_000_000_i64), + TestScalar::from(150_000_000_i64), + ]; + let expected = (Precision::new(11).unwrap(), 6, expected_scalars); + assert_eq!(expected, actual); + + let lhs = [0_i16, 2, 3]; + let rhs = [4_i16, 5, 2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::SmallInt; + let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(0_i64), + TestScalar::from(40_000_000_i64), + TestScalar::from(150_000_000_i64), + ]; + let expected = (Precision::new(13).unwrap(), 6, expected_scalars); + assert_eq!(expected, actual); + + // lhs is decimal with negative scale and rhs is integer + let lhs = [4_i8, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23]; + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let right_column_type = ColumnType::TinyInt; + let actual: (Precision, i8, Vec) = + try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(5_633_802), + TestScalar::from(-18_292_682), + TestScalar::from(-8_695_652), + ]; + let expected = (Precision::new(18).unwrap(), 6, expected_scalars); + assert_eq!(expected, actual); + + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23]; + let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); + let right_column_type = ColumnType::SmallInt; + let actual: (Precision, i8, Vec) = + try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(5_633_802), + TestScalar::from(-18_292_682), + TestScalar::from(-8_695_652), + ]; + let expected = (Precision::new(18).unwrap(), 6, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with nonnegative scale + let lhs = [4_i16, 2, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [3_i64, -5, 7] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(4).unwrap(), 2); + let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), 2); + let actual: (Precision, i8, Vec) = + try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(1_333_333), + TestScalar::from(-400_000), + TestScalar::from(-285_714), + ]; + let expected = (Precision::new(10).unwrap(), 6, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals one of which has negative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(2).unwrap(), -2); + let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), 3); + let actual: (Precision, i8, Vec) = + try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(5_633_802_816_i128), + TestScalar::from(-18_292_682_926_i128), + TestScalar::from(-8_695_652_173_i128), + ]; + let expected = (Precision::new(13).unwrap(), 6, expected_scalars); + assert_eq!(expected, actual); + + // lhs and rhs are both decimals with negative scale + let lhs = [4_i16, 15, -2] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let rhs = [71_i64, -82, 23] + .into_iter() + .map(TestScalar::from) + .collect::>(); + let left_column_type = ColumnType::Decimal75(Precision::new(2).unwrap(), -3); + let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), -2); + let actual: (Precision, i8, Vec) = + try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); + let expected_scalars = vec![ + TestScalar::from(563_380), + TestScalar::from(-1_829_268), + TestScalar::from(-869_565), + ]; + let expected = (Precision::new(9).unwrap(), 6, expected_scalars); + assert_eq!(expected, actual); + } +} diff --git a/crates/proof-of-sql/src/base/database/slice_operation.rs b/crates/proof-of-sql/src/base/database/slice_operation.rs index 276996e05..2c5b1ef73 100644 --- a/crates/proof-of-sql/src/base/database/slice_operation.rs +++ b/crates/proof-of-sql/src/base/database/slice_operation.rs @@ -1,23 +1,7 @@ -#![allow(dead_code)] use super::{ColumnOperationError, ColumnOperationResult}; -use crate::base::{ - database::{ - column_type_operation::{ - try_add_subtract_column_types, try_divide_column_types, try_multiply_column_types, - }, - ColumnType, - }, - math::decimal::Precision, - scalar::{Scalar, ScalarExt}, -}; use alloc::{format, vec::Vec}; -use core::{cmp::Ordering, fmt::Debug}; -use num_bigint::BigInt; -use num_traits::{ - ops::checked::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub}, - Zero, -}; -use proof_of_sql_parser::intermediate_ast::BinaryOperator; +use core::fmt::Debug; +use num_traits::ops::checked::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub}; // Unary operations @@ -350,467 +334,9 @@ where .collect() } -// Decimal operations - -/// Check whether a numerical slice is equal to a decimal one. -/// -/// Note that we do not check for length equality here. -/// # Panics -/// This function requires that `lhs` and `rhs` have the same length. -/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. -pub(super) fn eq_decimal_columns( - lhs: &[T], - rhs: &[S], - left_column_type: ColumnType, - right_column_type: ColumnType, -) -> Vec -where - S: Scalar, - T: Copy + Debug + PartialEq + Zero + Into, -{ - let lhs_scale = left_column_type.scale().expect("Numeric types have scale"); - let rhs_scale = right_column_type.scale().expect("Decimal types have scale"); - let max_scale = lhs_scale.max(rhs_scale); - // At most one of the scales is non-zero - if lhs_scale < max_scale { - // If scale difference is above max decimal precision values - // are equal if they are both zero and unequal otherwise - let upscale = max_scale - lhs_scale; - if i8::try_from( - right_column_type - .precision_value() - .expect("Decimal types have scale"), - ) - .is_ok_and(|precision| upscale > precision) - { - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { l.is_zero() && *r == S::ZERO }) - .collect::>() - } else { - let upscale_factor = - S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { Into::::into(*l) * upscale_factor == *r }) - .collect::>() - } - } else if rhs_scale < max_scale { - let upscale = max_scale - rhs_scale; - if i8::try_from( - left_column_type - .precision_value() - .expect("Numeric types have scale"), - ) - .is_ok_and(|precision| upscale > precision) - { - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { l.is_zero() && *r == S::ZERO }) - .collect::>() - } else { - let upscale_factor = - S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { Into::::into(*l) == *r * upscale_factor }) - .collect::>() - } - } else { - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { Into::::into(*l) == *r }) - .collect::>() - } -} - -/// Check whether a numerical slice is less than or equal to a decimal one. -/// -/// Note that we do not check for length equality here. -/// # Panics -/// This function requires that `lhs` and `rhs` have the same length. -/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. -pub(super) fn le_decimal_columns( - lhs: &[T], - rhs: &[S], - left_column_type: ColumnType, - right_column_type: ColumnType, -) -> Vec -where - S: Scalar, - T: Copy + Debug + Ord + Zero + Into, -{ - let lhs_scale = left_column_type.scale().expect("Numeric types have scale"); - let rhs_scale = right_column_type.scale().expect("Decimal types have scale"); - let max_scale = lhs_scale.max(rhs_scale); - // At most one of the scales is non-zero - if lhs_scale < max_scale { - // If scale difference is above max decimal precision the upscaled - // always have larger absolute value than the other one as long as it is nonzero - // Hence a (extremely upscaled) <= b if and only if a < 0 or (a == 0 and b >= 0) - let upscale = max_scale - lhs_scale; - if i8::try_from( - right_column_type - .precision_value() - .expect("Decimal types have scale"), - ) - .is_ok_and(|precision| upscale > precision) - { - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { - Into::::into(*l).signed_cmp(&S::ZERO) == Ordering::Less - || (l.is_zero() && r.signed_cmp(&S::ZERO) != Ordering::Less) - }) - .collect::>() - } else { - let upscale_factor = - S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { - (Into::::into(*l) * upscale_factor).signed_cmp(r) != Ordering::Greater - }) - .collect::>() - } - } else if rhs_scale < max_scale { - let upscale = max_scale - rhs_scale; - if i8::try_from( - left_column_type - .precision_value() - .expect("Numeric types have scale"), - ) - .is_ok_and(|precision| upscale > precision) - { - // Similarly with extreme scaling we have - // a <= (extremely upscaled) b if and only if a < 0 or (a == 0 and b >= 0) - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { - (Into::::into(*l).signed_cmp(&S::ZERO) != Ordering::Greater && *r == S::ZERO) - || r.signed_cmp(&S::ZERO) == Ordering::Greater - }) - .collect::>() - } else { - let upscale_factor = - S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { - Into::::into(*l).signed_cmp(&(*r * upscale_factor)) != Ordering::Greater - }) - .collect::>() - } - } else { - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { Into::::into(*l).signed_cmp(r) != Ordering::Greater }) - .collect::>() - } -} - -/// Check whether a numerical slice is greater than or equal to a decimal one. -/// -/// Note that we do not check for length equality here. -/// # Panics -/// This function requires that `lhs` and `rhs` have the same length. -/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. -pub(super) fn ge_decimal_columns( - lhs: &[T], - rhs: &[S], - left_column_type: ColumnType, - right_column_type: ColumnType, -) -> Vec -where - S: Scalar, - T: Copy + Debug + PartialEq + Zero + Into, -{ - let lhs_scale = left_column_type.scale().expect("Numeric types have scale"); - let rhs_scale = right_column_type.scale().expect("Decimal types have scale"); - let max_scale = lhs_scale.max(rhs_scale); - // At most one of the scales is non-zero - if lhs_scale < max_scale { - // If scale difference is above max decimal precision the upscaled - // always have larger absolute value than the other one as long as it is nonzero - // Hence a (extremely upscaled) >= b if and only if a > 0 or (a == 0 and b <= 0) - let upscale = max_scale - lhs_scale; - if i8::try_from( - right_column_type - .precision_value() - .expect("Decimal types have scale"), - ) - .is_ok_and(|precision| upscale > precision) - { - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { - Into::::into(*l).signed_cmp(&S::ZERO) == Ordering::Greater - || (l.is_zero() && r.signed_cmp(&S::ZERO) != Ordering::Greater) - }) - .collect::>() - } else { - let upscale_factor = - S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { - (Into::::into(*l) * upscale_factor).signed_cmp(r) != Ordering::Less - }) - .collect::>() - } - } else if rhs_scale < max_scale { - let upscale = max_scale - rhs_scale; - if i8::try_from( - left_column_type - .precision_value() - .expect("Numeric types have scale"), - ) - .is_ok_and(|precision| upscale > precision) - { - // Similarly with extreme scaling we have - // a >= (extremely upscaled) b if and only if b < 0 or (a >= 0 and b == 0) - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { - (Into::::into(*l).signed_cmp(&S::ZERO) != Ordering::Less && *r == S::ZERO) - || r.signed_cmp(&S::ZERO) == Ordering::Less - }) - .collect::>() - } else { - let upscale_factor = - S::pow10(u8::try_from(upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { - Into::::into(*l).signed_cmp(&(*r * upscale_factor)) != Ordering::Less - }) - .collect::>() - } - } else { - lhs.iter() - .zip(rhs.iter()) - .map(|(l, r)| -> bool { Into::::into(*l).signed_cmp(r) != Ordering::Less }) - .collect::>() - } -} - -/// Add two numerical slices as decimals. -/// -/// We do not check for length equality here -/// # Panics -/// This function requires that `lhs` and `rhs` have the same length. -/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. -pub(super) fn try_add_decimal_columns( - lhs: &[T0], - rhs: &[T1], - left_column_type: ColumnType, - right_column_type: ColumnType, -) -> ColumnOperationResult<(Precision, i8, Vec)> -where - S: Scalar + core::convert::From + core::convert::From, - T0: Copy, - T1: Copy, -{ - let new_column_type = - try_add_subtract_column_types(left_column_type, right_column_type, BinaryOperator::Add)?; - let new_precision_value = new_column_type - .precision_value() - .expect("numeric columns have precision"); - let new_scale = new_column_type.scale().expect("numeric columns have scale"); - let left_upscale = new_scale - - left_column_type - .scale() - .expect("numeric columns have scale"); - let right_upscale = new_scale - - right_column_type - .scale() - .expect("numeric columns have scale"); - // One of left_scale and right_scale is 0 so we can avoid scaling when unnecessary - let scalars: Vec = if left_upscale > 0 { - let upscale_factor = - S::pow10(u8::try_from(left_upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs) - .map(|(l, r)| S::from(*l) * upscale_factor + S::from(*r)) - .collect() - } else if right_upscale > 0 { - let upscale_factor = - S::pow10(u8::try_from(right_upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs) - .map(|(l, r)| S::from(*l) + upscale_factor * S::from(*r)) - .collect() - } else { - lhs.iter() - .zip(rhs) - .map(|(l, r)| S::from(*l) + S::from(*r)) - .collect() - }; - Ok(( - Precision::new(new_precision_value).expect("Precision value is valid"), - new_scale, - scalars, - )) -} - -/// Subtract one numerical slice from another as decimals. -/// -/// We do not check for length equality here -/// # Panics -/// This function requires that `lhs` and `rhs` have the same length. -/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. -pub(super) fn try_subtract_decimal_columns( - lhs: &[T0], - rhs: &[T1], - left_column_type: ColumnType, - right_column_type: ColumnType, -) -> ColumnOperationResult<(Precision, i8, Vec)> -where - S: Scalar + core::convert::From + core::convert::From, - T0: Copy, - T1: Copy, -{ - let new_column_type = try_add_subtract_column_types( - left_column_type, - right_column_type, - BinaryOperator::Subtract, - )?; - let new_precision_value = new_column_type - .precision_value() - .expect("numeric columns have precision"); - let new_scale = new_column_type.scale().expect("numeric columns have scale"); - let left_upscale = new_scale - - left_column_type - .scale() - .expect("numeric columns have scale"); - let right_upscale = new_scale - - right_column_type - .scale() - .expect("numeric columns have scale"); - // One of left_scale and right_scale is 0 so we can avoid scaling when unnecessary - let scalars: Vec = if left_upscale > 0 { - let upscale_factor = - S::pow10(u8::try_from(left_upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs) - .map(|(l, r)| S::from(*l) * upscale_factor - S::from(*r)) - .collect() - } else if right_upscale > 0 { - let upscale_factor = - S::pow10(u8::try_from(right_upscale).expect("Upscale factor is nonnegative")); - lhs.iter() - .zip(rhs) - .map(|(l, r)| S::from(*l) - upscale_factor * S::from(*r)) - .collect() - } else { - lhs.iter() - .zip(rhs) - .map(|(l, r)| S::from(*l) - S::from(*r)) - .collect() - }; - Ok(( - Precision::new(new_precision_value).expect("Precision value is valid"), - new_scale, - scalars, - )) -} - -/// Multiply two numerical slices as decimals. -/// -/// We do not check for length equality here -/// # Panics -/// This function requires that `lhs` and `rhs` have the same length. -/// This function requires that `left_column_type` and `right_column_type` have the same precision and scale. -pub(super) fn try_multiply_decimal_columns( - lhs: &[T0], - rhs: &[T1], - left_column_type: ColumnType, - right_column_type: ColumnType, -) -> ColumnOperationResult<(Precision, i8, Vec)> -where - S: Scalar + core::convert::From + core::convert::From, - T0: Copy, - T1: Copy, -{ - let new_column_type = try_multiply_column_types(left_column_type, right_column_type)?; - let new_precision_value = new_column_type - .precision_value() - .expect("numeric columns have precision"); - let new_scale = new_column_type.scale().expect("numeric columns have scale"); - let scalars: Vec = lhs - .iter() - .zip(rhs) - .map(|(l, r)| S::from(*l) * S::from(*r)) - .collect(); - Ok(( - Precision::new(new_precision_value).expect("Precision value is valid"), - new_scale, - scalars, - )) -} - -/// Divide an owned column by another. -/// -/// Notes: -/// 1. We do not check for length equality here. -/// 2. We use floor division for rounding. -/// 3. If division by zero occurs, we return an error. -/// 4. Precision and scale follow T-SQL rules. That is, -/// - `new_scale = max(6, right_precision + left_scale + 1)` -/// - `new_precision = left_precision - left_scale + right_scale + new_scale` -#[allow(clippy::missing_panics_doc)] -pub(crate) fn try_divide_decimal_columns( - lhs: &[T0], - rhs: &[T1], - left_column_type: ColumnType, - right_column_type: ColumnType, -) -> ColumnOperationResult<(Precision, i8, Vec)> -where - S: Scalar, - T0: Copy + Debug + Into, - T1: Copy + Debug + Into, -{ - let new_column_type = try_divide_column_types(left_column_type, right_column_type)?; - let new_precision_value = new_column_type - .precision_value() - .expect("numeric columns have precision"); - let new_scale = new_column_type.scale().expect("numeric columns have scale"); - let lhs_scale = left_column_type - .scale() - .expect("numeric columns have scale"); - let rhs_scale = right_column_type - .scale() - .expect("numeric columns have scale"); - let applied_scale = rhs_scale - lhs_scale + new_scale; - let applied_scale_factor = BigInt::from(10).pow(u32::from(applied_scale.unsigned_abs())); - let result: Vec = lhs - .iter() - .zip(rhs) - .map(|(l, r)| -> ColumnOperationResult { - let lhs_bigint = Into::::into(*l); - let rhs_bigint = Into::::into(*r); - if rhs_bigint.is_zero() { - return Err(ColumnOperationError::DivisionByZero); - } - let new_bigint = if applied_scale >= 0 { - lhs_bigint * &applied_scale_factor / rhs_bigint - } else { - lhs_bigint / rhs_bigint / &applied_scale_factor - }; - Ok(S::try_from(new_bigint).expect("Division result should fit into scalar")) - }) - .collect::>>()?; - Ok(( - Precision::new(new_precision_value).expect("Precision value is valid"), - new_scale, - result, - )) -} - #[cfg(test)] mod test { use super::*; - use crate::base::scalar::test_scalar::TestScalar; // NOT #[test] @@ -867,119 +393,6 @@ mod test { assert_eq!(expected, actual); } - #[test] - fn we_can_eq_decimal_columns() { - // lhs is integer and rhs is decimal with nonnegative scale - let lhs = [1_i8, -2, 3]; - let rhs = [100_i8, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::TinyInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, false, false]; - assert_eq!(expected, actual); - - let lhs = [1_i16, -2, 3]; - let rhs = [100_i16, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::SmallInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, false, false]; - assert_eq!(expected, actual); - - // lhs is integer and rhs is decimal with negative scale - let lhs = [400_i64, -82, -200]; - let rhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::BigInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, false, true]; - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = [4_i16, -80, 230] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -8, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, true]; - assert_eq!(expected, actual); - - // lhs is decimal with negative scale and rhs is decimal with nonnegative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, 150_000, -20000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); - let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, true]; - assert_eq!(expected, actual); - - // lhs is decimal with nonnegative scale and rhs is decimal with negative scale - let lhs = [71_i64, 150_000, -20000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, true]; - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with negative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, 150_000, -20000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), -46); - let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, true]; - assert_eq!(expected, actual); - - // lhs and rhs are decimals with extreme differences in scale - let lhs = [4_i16, 0, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, 0, -20000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 26); - let actual = eq_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, false]; - assert_eq!(expected, actual); - } - // <= #[test] fn we_can_le_slices() { @@ -999,119 +412,6 @@ mod test { assert_eq!(expected, actual); } - #[test] - fn we_can_le_decimal_columns() { - // lhs is integer and rhs is decimal with nonnegative scale - let lhs = [1_i8, -2, 3]; - let rhs = [100_i8, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::TinyInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, true, false]; - assert_eq!(expected, actual); - - let lhs = [1_i16, -2, 3]; - let rhs = [100_i16, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::SmallInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, true, false]; - assert_eq!(expected, actual); - - // lhs is integer and rhs is decimal with negative scale - let lhs = [400_i64, -82, -199]; - let rhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::BigInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, true, false]; - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = [4_i16, -80, 230] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -8, 22] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, true, false]; - assert_eq!(expected, actual); - - // lhs is decimal with negative scale and rhs is decimal with nonnegative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, 150_000, -30000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); - let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, false]; - assert_eq!(expected, actual); - - // lhs is decimal with nonnegative scale and rhs is decimal with negative scale - let lhs = [71_i64, 150_000, -19000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, true, false]; - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with negative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71000_i64, 150_000, -21000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), -46); - let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, true, false]; - assert_eq!(expected, actual); - - // lhs and rhs are decimals with extreme differences in scale - let lhs = [1_i16, 1, 1, 0, 0, 0, -1, -1, -1] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [1_i64, 0, -1, 1, 0, -1, 1, 0, -1] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 26); - let actual = le_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, false, false, true, true, false, true, true, true]; - assert_eq!(expected, actual); - } - // >= #[test] fn we_can_ge_slices() { @@ -1131,119 +431,6 @@ mod test { assert_eq!(expected, actual); } - #[test] - fn we_can_ge_decimal_columns() { - // lhs is integer and rhs is decimal with nonnegative scale - let lhs = [1_i8, -2, 3]; - let rhs = [100_i8, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::TinyInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, false, true]; - assert_eq!(expected, actual); - - let lhs = [1_i16, -2, 3]; - let rhs = [100_i16, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::SmallInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, false, true]; - assert_eq!(expected, actual); - - // lhs is integer and rhs is decimal with negative scale - let lhs = [400_i64, -82, 199]; - let rhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::BigInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, false, true]; - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = [4_i16, -80, 230] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -8, -22] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, true]; - assert_eq!(expected, actual); - - // lhs is decimal with negative scale and rhs is decimal with nonnegative scale - let lhs = [-4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, 150_000, -30000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); - let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, true]; - assert_eq!(expected, actual); - - // lhs is decimal with nonnegative scale and rhs is decimal with negative scale - let lhs = [71_i64, 150_000, -19000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 2); - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, true]; - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with negative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71000_i64, 150_000, -21000] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), -46); - let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![false, true, true]; - assert_eq!(expected, actual); - - // lhs and rhs are decimals with extreme differences in scale - let lhs = [1_i16, 1, 1, 0, 0, 0, -1, -1, -1] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [1_i64, 0, -1, 1, 0, -1, 1, 0, -1] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -50); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), 26); - let actual = ge_decimal_columns(&lhs, &rhs, left_column_type, right_column_type); - let expected = vec![true, true, true, false, true, true, false, false, false]; - assert_eq!(expected, actual); - } - // + #[test] fn we_can_try_add_slices() { @@ -1283,128 +470,6 @@ mod test { )); } - #[allow(clippy::too_many_lines)] - #[test] - fn we_can_try_add_decimal_columns() { - // lhs is integer and rhs is decimal with nonnegative scale - let lhs = [1_i8, -2, 3]; - let rhs = [4_i8, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::TinyInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(104), - TestScalar::from(-195), - TestScalar::from(298), - ]; - let expected = (Precision::new(11).unwrap(), 2, expected_scalars); - assert_eq!(expected, actual); - - let lhs = [1_i16, -2, 3]; - let rhs = [4_i16, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::SmallInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(104), - TestScalar::from(-195), - TestScalar::from(298), - ]; - let expected = (Precision::new(11).unwrap(), 2, expected_scalars); - assert_eq!(expected, actual); - - // lhs is decimal with negative scale and rhs is integer - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23]; - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let right_column_type = ColumnType::BigInt; - let actual: (Precision, i8, Vec) = - try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(471), - TestScalar::from(1418), - TestScalar::from(-177), - ]; - let expected = (Precision::new(20).unwrap(), 0, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(12).unwrap(), 2); - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); - let actual: (Precision, i8, Vec) = - try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(111), - TestScalar::from(68), - TestScalar::from(3), - ]; - let expected = (Precision::new(14).unwrap(), 3, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals one of which has negative scale - // and with result having maximum precision - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(69).unwrap(), -2); - let right_column_type = ColumnType::Decimal75(Precision::new(50).unwrap(), 3); - let actual: (Precision, i8, Vec) = - try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(400_071), - TestScalar::from(1_499_918), - TestScalar::from(-199_977), - ]; - let expected = (Precision::new(75).unwrap(), 3, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with negative scale - // and with result having maximum precision and minimum scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(74).unwrap(), -128); - let right_column_type = ColumnType::Decimal75(Precision::new(74).unwrap(), -128); - let actual: (Precision, i8, Vec) = - try_add_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(75), - TestScalar::from(-67), - TestScalar::from(21), - ]; - let expected = (Precision::new(75).unwrap(), -128, expected_scalars); - assert_eq!(expected, actual); - } - // - #[test] fn we_can_try_subtract_slices() { @@ -1463,128 +528,6 @@ mod test { )); } - #[allow(clippy::too_many_lines)] - #[test] - fn we_can_try_subtract_decimal_columns() { - // lhs is integer and rhs is decimal with nonnegative scale - let lhs = [1_i8, -2, 3]; - let rhs = [4_i8, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::TinyInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(96), - TestScalar::from(-205), - TestScalar::from(302), - ]; - let expected = (Precision::new(11).unwrap(), 2, expected_scalars); - assert_eq!(expected, actual); - - let lhs = [1_i16, -2, 3]; - let rhs = [4_i16, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::SmallInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(96), - TestScalar::from(-205), - TestScalar::from(302), - ]; - let expected = (Precision::new(11).unwrap(), 2, expected_scalars); - assert_eq!(expected, actual); - - // lhs is decimal with negative scale and rhs is integer - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23]; - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let right_column_type = ColumnType::BigInt; - let actual: (Precision, i8, Vec) = - try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(329), - TestScalar::from(1582), - TestScalar::from(-223), - ]; - let expected = (Precision::new(20).unwrap(), 0, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(12).unwrap(), 2); - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 3); - let actual: (Precision, i8, Vec) = - try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(-31), - TestScalar::from(232), - TestScalar::from(-43), - ]; - let expected = (Precision::new(14).unwrap(), 3, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals one of which has negative scale - // and with result having maximum precision - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(69).unwrap(), -2); - let right_column_type = ColumnType::Decimal75(Precision::new(50).unwrap(), 3); - let actual: (Precision, i8, Vec) = - try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(399_929), - TestScalar::from(1_500_082), - TestScalar::from(-200_023), - ]; - let expected = (Precision::new(75).unwrap(), 3, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with negative scale - // and with result having maximum precision and minimum scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(74).unwrap(), -128); - let right_column_type = ColumnType::Decimal75(Precision::new(74).unwrap(), -128); - let actual: (Precision, i8, Vec) = - try_subtract_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(-67), - TestScalar::from(97), - TestScalar::from(-25), - ]; - let expected = (Precision::new(75).unwrap(), -128, expected_scalars); - assert_eq!(expected, actual); - } - // * #[test] fn we_can_try_multiply_slices() { @@ -1624,129 +567,6 @@ mod test { )); } - #[allow(clippy::too_many_lines)] - #[test] - fn we_can_try_multiply_decimal_columns() { - // lhs is integer and rhs is decimal with nonnegative scale - let lhs = [1_i8, -2, 3]; - let rhs = [4_i8, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::TinyInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(4), - TestScalar::from(-10), - TestScalar::from(-6), - ]; - let expected = (Precision::new(14).unwrap(), 2, expected_scalars); - assert_eq!(expected, actual); - - let lhs = [1_i16, -2, 3]; - let rhs = [4_i16, 5, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::SmallInt; - let right_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(4), - TestScalar::from(-10), - TestScalar::from(-6), - ]; - let expected = (Precision::new(16).unwrap(), 2, expected_scalars); - assert_eq!(expected, actual); - - // lhs is decimal with negative scale and rhs is integer - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23]; - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let right_column_type = ColumnType::BigInt; - let actual: (Precision, i8, Vec) = - try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(284), - TestScalar::from(-1230), - TestScalar::from(-46), - ]; - let expected = (Precision::new(30).unwrap(), -2, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - // and with result having maximum precision and maximum scale - let lhs = [4_i16, 25, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(42).unwrap(), 72); - let right_column_type = ColumnType::Decimal75(Precision::new(32).unwrap(), 55); - let actual: (Precision, i8, Vec) = - try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(284), - TestScalar::from(-2050), - TestScalar::from(-46), - ]; - let expected = (Precision::new(75).unwrap(), 127, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals one of which has negative scale - // and with result having maximum precision - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(69).unwrap(), -2); - let right_column_type = ColumnType::Decimal75(Precision::new(5).unwrap(), 3); - let actual: (Precision, i8, Vec) = - try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(284), - TestScalar::from(-1230), - TestScalar::from(-46), - ]; - let expected = (Precision::new(75).unwrap(), 1, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with negative scale - // and with result having maximum precision and minimum scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(34).unwrap(), -64); - let right_column_type = ColumnType::Decimal75(Precision::new(40).unwrap(), -64); - let actual: (Precision, i8, Vec) = - try_multiply_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(284), - TestScalar::from(-1230), - TestScalar::from(-46), - ]; - let expected = (Precision::new(75).unwrap(), -128, expected_scalars); - assert_eq!(expected, actual); - } - // / #[test] fn we_can_try_divide_slices() { @@ -1804,141 +624,4 @@ mod test { Err(ColumnOperationError::DivisionByZero) )); } - - #[allow(clippy::too_many_lines)] - #[test] - fn we_can_try_divide_decimal_columns() { - // lhs is integer and rhs is decimal with nonnegative scale - let lhs = [0_i8, 2, 3]; - let rhs = [4_i8, 5, 2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::TinyInt; - let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(0_i64), - TestScalar::from(40_000_000_i64), - TestScalar::from(150_000_000_i64), - ]; - let expected = (Precision::new(11).unwrap(), 6, expected_scalars); - assert_eq!(expected, actual); - - let lhs = [0_i16, 2, 3]; - let rhs = [4_i16, 5, 2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::SmallInt; - let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(0_i64), - TestScalar::from(40_000_000_i64), - TestScalar::from(150_000_000_i64), - ]; - let expected = (Precision::new(13).unwrap(), 6, expected_scalars); - assert_eq!(expected, actual); - - // lhs is decimal with negative scale and rhs is integer - let lhs = [4_i8, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23]; - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let right_column_type = ColumnType::TinyInt; - let actual: (Precision, i8, Vec) = - try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(5_633_802), - TestScalar::from(-18_292_682), - TestScalar::from(-8_695_652), - ]; - let expected = (Precision::new(18).unwrap(), 6, expected_scalars); - assert_eq!(expected, actual); - - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23]; - let left_column_type = ColumnType::Decimal75(Precision::new(10).unwrap(), -2); - let right_column_type = ColumnType::SmallInt; - let actual: (Precision, i8, Vec) = - try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(5_633_802), - TestScalar::from(-18_292_682), - TestScalar::from(-8_695_652), - ]; - let expected = (Precision::new(18).unwrap(), 6, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with nonnegative scale - let lhs = [4_i16, 2, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [3_i64, -5, 7] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(4).unwrap(), 2); - let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), 2); - let actual: (Precision, i8, Vec) = - try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(1_333_333), - TestScalar::from(-400_000), - TestScalar::from(-285_714), - ]; - let expected = (Precision::new(10).unwrap(), 6, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals one of which has negative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(2).unwrap(), -2); - let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), 3); - let actual: (Precision, i8, Vec) = - try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(5_633_802_816_i128), - TestScalar::from(-18_292_682_926_i128), - TestScalar::from(-8_695_652_173_i128), - ]; - let expected = (Precision::new(13).unwrap(), 6, expected_scalars); - assert_eq!(expected, actual); - - // lhs and rhs are both decimals with negative scale - let lhs = [4_i16, 15, -2] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let rhs = [71_i64, -82, 23] - .into_iter() - .map(TestScalar::from) - .collect::>(); - let left_column_type = ColumnType::Decimal75(Precision::new(2).unwrap(), -3); - let right_column_type = ColumnType::Decimal75(Precision::new(3).unwrap(), -2); - let actual: (Precision, i8, Vec) = - try_divide_decimal_columns(&lhs, &rhs, left_column_type, right_column_type).unwrap(); - let expected_scalars = vec![ - TestScalar::from(563_380), - TestScalar::from(-1_829_268), - TestScalar::from(-869_565), - ]; - let expected = (Precision::new(9).unwrap(), 6, expected_scalars); - assert_eq!(expected, actual); - } }