diff --git a/arrow-schema/src/datatype.rs b/arrow-schema/src/datatype.rs index f22b6c52ba34..f742d99cda4a 100644 --- a/arrow-schema/src/datatype.rs +++ b/arrow-schema/src/datatype.rs @@ -467,7 +467,7 @@ impl fmt::Display for DataType { .map(|f| format!("{} {}", f.name(), f.data_type())) .collect::>() .join(", "); - write!(f, "{}", fields_str)?; + write!(f, "{fields_str}")?; } write!(f, ")")?; Ok(()) diff --git a/arrow-schema/src/datatype_parse.rs b/arrow-schema/src/datatype_parse.rs index 70e4b351ff50..d0fc962fb150 100644 --- a/arrow-schema/src/datatype_parse.rs +++ b/arrow-schema/src/datatype_parse.rs @@ -79,10 +79,9 @@ impl<'a> Parser<'a> { Token::LargeList => self.parse_large_list(), Token::FixedSizeList => self.parse_fixed_size_list(), Token::Struct => self.parse_struct(), - Token::FieldName(word) => Err(make_error( - self.val, - &format!("unrecognized word: {}", word), - )), + Token::FieldName(word) => { + Err(make_error(self.val, &format!("unrecognized word: {word}"))) + } tok => Err(make_error( self.val, &format!("finding next type, got unexpected '{tok}'"), @@ -155,10 +154,9 @@ impl<'a> Parser<'a> { fn parse_double_quoted_string(&mut self, context: &str) -> ArrowResult { match self.next_token()? { Token::DoubleQuotedString(s) => Ok(s), - Token::FieldName(word) => Err(make_error( - self.val, - &format!("unrecognized word: {}", word), - )), + Token::FieldName(word) => { + Err(make_error(self.val, &format!("unrecognized word: {word}"))) + } tok => Err(make_error( self.val, &format!("finding double quoted string for {context}, got '{tok}'"), diff --git a/arrow-schema/src/fields.rs b/arrow-schema/src/fields.rs index 904b933cd299..65b79660e6fe 100644 --- a/arrow-schema/src/fields.rs +++ b/arrow-schema/src/fields.rs @@ -365,7 +365,7 @@ impl UnionFields { .inspect(|&idx| { let mask = 1_u128 << idx; if (set & mask) != 0 { - panic!("duplicate type id: {}", idx); + panic!("duplicate type id: {idx}"); } else { set |= mask; } diff --git a/parquet-variant/src/builder.rs b/parquet-variant/src/builder.rs index 73197e612483..d67ab9c00165 100644 --- a/parquet-variant/src/builder.rs +++ b/parquet-variant/src/builder.rs @@ -855,7 +855,7 @@ mod tests { let val2 = list.get(2).unwrap(); assert_eq!(val2, Variant::ShortString(ShortString("test"))); } - _ => panic!("Expected an array variant, got: {:?}", variant), + _ => panic!("Expected an array variant, got: {variant:?}"), } } diff --git a/parquet-variant/src/decoder.rs b/parquet-variant/src/decoder.rs index cb8336b5b88d..e73911aa2953 100644 --- a/parquet-variant/src/decoder.rs +++ b/parquet-variant/src/decoder.rs @@ -95,8 +95,7 @@ impl TryFrom for VariantPrimitiveType { 15 => Ok(VariantPrimitiveType::Binary), 16 => Ok(VariantPrimitiveType::String), _ => Err(ArrowError::InvalidArgumentError(format!( - "unknown primitive type: {}", - value + "unknown primitive type: {value}", ))), } } diff --git a/parquet-variant/src/to_json.rs b/parquet-variant/src/to_json.rs index 9e5cbdccefab..09efe20a7abc 100644 --- a/parquet-variant/src/to_json.rs +++ b/parquet-variant/src/to_json.rs @@ -41,51 +41,7 @@ fn format_binary_base64(bytes: &[u8]) -> String { general_purpose::STANDARD.encode(bytes) } -/// Write decimal using scovich's hybrid approach for i32 -fn write_decimal_i32( - json_buffer: &mut impl Write, - integer: i32, - scale: u8, -) -> Result<(), ArrowError> { - let integer = if scale == 0 { - integer - } else { - let divisor = 10_i32.pow(scale as u32); - if integer % divisor != 0 { - // fall back to floating point - let result = integer as f64 / divisor as f64; - write!(json_buffer, "{}", result)?; - return Ok(()); - } - integer / divisor - }; - write!(json_buffer, "{}", integer)?; - Ok(()) -} - -/// Write decimal using scovich's hybrid approach for i64 -fn write_decimal_i64( - json_buffer: &mut impl Write, - integer: i64, - scale: u8, -) -> Result<(), ArrowError> { - let integer = if scale == 0 { - integer - } else { - let divisor = 10_i64.pow(scale as u32); - if integer % divisor != 0 { - // fall back to floating point - let result = integer as f64 / divisor as f64; - write!(json_buffer, "{}", result)?; - return Ok(()); - } - integer / divisor - }; - write!(json_buffer, "{}", integer)?; - Ok(()) -} - -/// Converts a Variant to JSON and writes it to the provided [`Write`] +/// Converts a Variant to JSON and writes it to the provided `Write` /// /// This function writes JSON directly to any type that implements [`Write`], /// making it efficient for streaming or when you want to control the output destination. @@ -140,40 +96,15 @@ pub fn variant_to_json(json_buffer: &mut impl Write, variant: &Variant) -> Resul Variant::Null => write!(json_buffer, "null")?, Variant::BooleanTrue => write!(json_buffer, "true")?, Variant::BooleanFalse => write!(json_buffer, "false")?, - Variant::Int8(i) => write!(json_buffer, "{}", i)?, - Variant::Int16(i) => write!(json_buffer, "{}", i)?, - Variant::Int32(i) => write!(json_buffer, "{}", i)?, - Variant::Int64(i) => write!(json_buffer, "{}", i)?, - Variant::Float(f) => write!(json_buffer, "{}", f)?, - Variant::Double(f) => write!(json_buffer, "{}", f)?, - Variant::Decimal4(VariantDecimal4 { integer, scale }) => { - write_decimal_i32(json_buffer, *integer, *scale)?; - } - Variant::Decimal8(VariantDecimal8 { integer, scale }) => { - write_decimal_i64(json_buffer, *integer, *scale)?; - } - Variant::Decimal16(VariantDecimal16 { integer, scale }) => { - let integer = if *scale == 0 { - *integer - } else { - let divisor = 10_i128.pow(*scale as u32); - if integer % divisor != 0 { - // fall back to floating point - let result = *integer as f64 / divisor as f64; - write!(json_buffer, "{}", result)?; - return Ok(()); - } - integer / divisor - }; - // Prefer to emit as i64, but fall back to u64 or even f64 (lossy) if necessary - if let Ok(i64_val) = i64::try_from(integer) { - write!(json_buffer, "{}", i64_val)?; - } else if let Ok(u64_val) = u64::try_from(integer) { - write!(json_buffer, "{}", u64_val)?; - } else { - write!(json_buffer, "{}", integer as f64)?; - } - } + Variant::Int8(i) => write!(json_buffer, "{i}")?, + Variant::Int16(i) => write!(json_buffer, "{i}")?, + Variant::Int32(i) => write!(json_buffer, "{i}")?, + Variant::Int64(i) => write!(json_buffer, "{i}")?, + Variant::Float(f) => write!(json_buffer, "{f}")?, + Variant::Double(f) => write!(json_buffer, "{f}")?, + Variant::Decimal4(decimal) => write!(json_buffer, "{decimal}")?, + Variant::Decimal8(decimal) => write!(json_buffer, "{decimal}")?, + Variant::Decimal16(decimal) => write!(json_buffer, "{decimal}")?, Variant::Date(date) => write!(json_buffer, "\"{}\"", format_date_string(date))?, Variant::TimestampMicros(ts) => write!(json_buffer, "\"{}\"", ts.to_rfc3339())?, Variant::TimestampNtzMicros(ts) => { @@ -183,23 +114,23 @@ pub fn variant_to_json(json_buffer: &mut impl Write, variant: &Variant) -> Resul // Encode binary as base64 string let base64_str = format_binary_base64(bytes); let json_str = serde_json::to_string(&base64_str).map_err(|e| { - ArrowError::InvalidArgumentError(format!("JSON encoding error: {}", e)) + ArrowError::InvalidArgumentError(format!("JSON encoding error: {e}")) })?; - write!(json_buffer, "{}", json_str)? + write!(json_buffer, "{json_str}")? } Variant::String(s) => { // Use serde_json to properly escape the string let json_str = serde_json::to_string(s).map_err(|e| { - ArrowError::InvalidArgumentError(format!("JSON encoding error: {}", e)) + ArrowError::InvalidArgumentError(format!("JSON encoding error: {e}")) })?; - write!(json_buffer, "{}", json_str)? + write!(json_buffer, "{json_str}")? } Variant::ShortString(s) => { // Use serde_json to properly escape the string let json_str = serde_json::to_string(s.as_str()).map_err(|e| { - ArrowError::InvalidArgumentError(format!("JSON encoding error: {}", e)) + ArrowError::InvalidArgumentError(format!("JSON encoding error: {e}")) })?; - write!(json_buffer, "{}", json_str)? + write!(json_buffer, "{json_str}")? } Variant::Object(obj) => { convert_object_to_json(json_buffer, obj)?; @@ -226,9 +157,9 @@ fn convert_object_to_json(buffer: &mut impl Write, obj: &VariantObject) -> Resul // Write the key (properly escaped) let json_key = serde_json::to_string(key).map_err(|e| { - ArrowError::InvalidArgumentError(format!("JSON key encoding error: {}", e)) + ArrowError::InvalidArgumentError(format!("JSON key encoding error: {e}")) })?; - write!(buffer, "{}:", json_key)?; + write!(buffer, "{json_key}:")?; // Recursively convert the value variant_to_json(buffer, &value)?; @@ -313,7 +244,7 @@ pub fn variant_to_json_string(variant: &Variant) -> Result { let mut buffer = Vec::new(); variant_to_json(&mut buffer, variant)?; String::from_utf8(buffer) - .map_err(|e| ArrowError::InvalidArgumentError(format!("UTF-8 conversion error: {}", e))) + .map_err(|e| ArrowError::InvalidArgumentError(format!("UTF-8 conversion error: {e}"))) } /// Convert [`Variant`] to [`serde_json::Value`] @@ -394,7 +325,8 @@ pub fn variant_to_json_value(variant: &Variant) -> Result { } integer / divisor }; - // Prefer to emit as i64, but fall back to u64 or even f64 (lossy) if necessary + // i128 has higher precision than any 64-bit type. Try a lossless narrowing cast to + // i64 or u64 first, falling back to a lossy narrowing cast to f64 if necessary. let value = i64::try_from(integer) .map(Value::from) .or_else(|_| u64::try_from(integer).map(Value::from)) @@ -928,8 +860,7 @@ mod tests { let json = variant_to_json_string(&variant)?; // Parse the JSON to verify structure - handle JSON parsing errors manually - let parsed: Value = serde_json::from_str(&json) - .map_err(|e| ArrowError::ParseError(format!("JSON parse error: {}", e)))?; + let parsed: Value = serde_json::from_str(&json).unwrap(); let obj = parsed.as_object().expect("expected JSON object"); assert_eq!(obj.get("name"), Some(&Value::String("Alice".to_string()))); assert_eq!(obj.get("age"), Some(&Value::Number(30.into()))); @@ -990,8 +921,7 @@ mod tests { assert!(json.contains("😀 Smiley")); // Verify that the JSON can be parsed back - let parsed: Value = serde_json::from_str(&json) - .map_err(|e| ArrowError::ParseError(format!("JSON parse error: {}", e)))?; + let parsed: Value = serde_json::from_str(&json).unwrap(); assert!(matches!(parsed, Value::Object(_))); Ok(()) @@ -1069,8 +999,7 @@ mod tests { let variant = Variant::try_new(&metadata, &value)?; let json = variant_to_json_string(&variant)?; - let parsed: Value = serde_json::from_str(&json) - .map_err(|e| ArrowError::ParseError(format!("JSON parse error: {}", e)))?; + let parsed: Value = serde_json::from_str(&json).unwrap(); let arr = parsed.as_array().expect("expected JSON array"); assert_eq!(arr.len(), 5); assert_eq!(arr[0], Value::String("hello".to_string())); @@ -1102,8 +1031,7 @@ mod tests { let json = variant_to_json_string(&variant)?; // Parse and verify all fields are present - let parsed: Value = serde_json::from_str(&json) - .map_err(|e| ArrowError::ParseError(format!("JSON parse error: {}", e)))?; + let parsed: Value = serde_json::from_str(&json).unwrap(); let obj = parsed.as_object().expect("expected JSON object"); assert_eq!(obj.len(), 3); assert_eq!(obj.get("alpha"), Some(&Value::String("first".to_string()))); @@ -1135,8 +1063,7 @@ mod tests { let variant = Variant::try_new(&metadata, &value)?; let json = variant_to_json_string(&variant)?; - let parsed: Value = serde_json::from_str(&json) - .map_err(|e| ArrowError::ParseError(format!("JSON parse error: {}", e)))?; + let parsed: Value = serde_json::from_str(&json).unwrap(); let arr = parsed.as_array().expect("expected JSON array"); assert_eq!(arr.len(), 7); assert_eq!(arr[0], Value::String("string_value".to_string())); @@ -1171,8 +1098,7 @@ mod tests { let variant = Variant::try_new(&metadata, &value)?; let json = variant_to_json_string(&variant)?; - let parsed: Value = serde_json::from_str(&json) - .map_err(|e| ArrowError::ParseError(format!("JSON parse error: {}", e)))?; + let parsed: Value = serde_json::from_str(&json).unwrap(); let obj = parsed.as_object().expect("expected JSON object"); assert_eq!(obj.len(), 6); assert_eq!( @@ -1202,8 +1128,7 @@ mod tests { // Due to f64 precision limits, we expect precision loss for values > 2^53 // Both functions should produce consistent results (even if not exact) - let parsed: Value = serde_json::from_str(&json_string) - .map_err(|e| ArrowError::ParseError(format!("JSON parse error: {}", e)))?; + let parsed: Value = serde_json::from_str(&json_string).unwrap(); assert_eq!(parsed, json_value); // Test a case that can be exactly represented (integer result) diff --git a/parquet-variant/src/variant/decimal.rs b/parquet-variant/src/variant/decimal.rs index f03b1e1e388d..c92fd1df8293 100644 --- a/parquet-variant/src/variant/decimal.rs +++ b/parquet-variant/src/variant/decimal.rs @@ -15,6 +15,30 @@ // specific language governing permissions and limitations // under the License. use arrow_schema::ArrowError; +use std::fmt; + +// Macro to format decimal values, using only integer arithmetic to avoid floating point precision loss +macro_rules! format_decimal { + ($f:expr, $integer:expr, $scale:expr, $int_type:ty) => {{ + let integer = if $scale == 0 { + $integer + } else { + let divisor = (10 as $int_type).pow($scale as u32); + let remainder = $integer % divisor; + if remainder != 0 { + // Track the sign explicitly, in case the quotient is zero + let sign = if $integer < 0 { "-" } else { "" }; + // Format an unsigned remainder with leading zeros and strip (unnecessary) trailing zeros. + let remainder = format!("{:0width$}", remainder.abs(), width = $scale as usize); + let remainder = remainder.trim_end_matches('0'); + let quotient = $integer / divisor; + return write!($f, "{}{}.{}", sign, quotient.abs(), remainder); + } + $integer / divisor + }; + write!($f, "{}", integer) + }}; +} /// Represents a 4-byte decimal value in the Variant format. /// @@ -57,6 +81,12 @@ impl VariantDecimal4 { } } +impl fmt::Display for VariantDecimal4 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + format_decimal!(f, self.integer, self.scale, i32) + } +} + /// Represents an 8-byte decimal value in the Variant format. /// /// This struct stores a decimal number using a 64-bit signed integer for the coefficient @@ -99,6 +129,12 @@ impl VariantDecimal8 { } } +impl fmt::Display for VariantDecimal8 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + format_decimal!(f, self.integer, self.scale, i64) + } +} + /// Represents an 16-byte decimal value in the Variant format. /// /// This struct stores a decimal number using a 128-bit signed integer for the coefficient @@ -141,6 +177,12 @@ impl VariantDecimal16 { } } +impl fmt::Display for VariantDecimal16 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + format_decimal!(f, self.integer, self.scale, i128) + } +} + #[cfg(test)] mod tests { use super::*; @@ -328,4 +370,211 @@ mod tests { "Decimal16 with scale = 38 should succeed" ); } + + #[test] + fn test_variant_decimal4_display() { + // Test zero scale (integers) + let d = VariantDecimal4::try_new(42, 0).unwrap(); + assert_eq!(d.to_string(), "42"); + + let d = VariantDecimal4::try_new(-42, 0).unwrap(); + assert_eq!(d.to_string(), "-42"); + + // Test basic decimal formatting + let d = VariantDecimal4::try_new(12345, 2).unwrap(); + assert_eq!(d.to_string(), "123.45"); + + let d = VariantDecimal4::try_new(-12345, 2).unwrap(); + assert_eq!(d.to_string(), "-123.45"); + + // Test trailing zeros are trimmed + let d = VariantDecimal4::try_new(12300, 2).unwrap(); + assert_eq!(d.to_string(), "123"); + + let d = VariantDecimal4::try_new(-12300, 2).unwrap(); + assert_eq!(d.to_string(), "-123"); + + // Test leading zeros in decimal part + let d = VariantDecimal4::try_new(1005, 3).unwrap(); + assert_eq!(d.to_string(), "1.005"); + + let d = VariantDecimal4::try_new(-1005, 3).unwrap(); + assert_eq!(d.to_string(), "-1.005"); + + // Test number smaller than scale (leading zero before decimal) + let d = VariantDecimal4::try_new(123, 4).unwrap(); + assert_eq!(d.to_string(), "0.0123"); + + let d = VariantDecimal4::try_new(-123, 4).unwrap(); + assert_eq!(d.to_string(), "-0.0123"); + + // Test zero + let d = VariantDecimal4::try_new(0, 0).unwrap(); + assert_eq!(d.to_string(), "0"); + + let d = VariantDecimal4::try_new(0, 3).unwrap(); + assert_eq!(d.to_string(), "0"); + + // Test max scale + let d = VariantDecimal4::try_new(123456789, 9).unwrap(); + assert_eq!(d.to_string(), "0.123456789"); + + let d = VariantDecimal4::try_new(-123456789, 9).unwrap(); + assert_eq!(d.to_string(), "-0.123456789"); + + // Test max precision + let d = VariantDecimal4::try_new(999999999, 0).unwrap(); + assert_eq!(d.to_string(), "999999999"); + + let d = VariantDecimal4::try_new(-999999999, 0).unwrap(); + assert_eq!(d.to_string(), "-999999999"); + + // Test trailing zeros with mixed decimal places + let d = VariantDecimal4::try_new(120050, 4).unwrap(); + assert_eq!(d.to_string(), "12.005"); + + let d = VariantDecimal4::try_new(-120050, 4).unwrap(); + assert_eq!(d.to_string(), "-12.005"); + } + + #[test] + fn test_variant_decimal8_display() { + // Test zero scale (integers) + let d = VariantDecimal8::try_new(42, 0).unwrap(); + assert_eq!(d.to_string(), "42"); + + let d = VariantDecimal8::try_new(-42, 0).unwrap(); + assert_eq!(d.to_string(), "-42"); + + // Test basic decimal formatting + let d = VariantDecimal8::try_new(1234567890, 3).unwrap(); + assert_eq!(d.to_string(), "1234567.89"); + + let d = VariantDecimal8::try_new(-1234567890, 3).unwrap(); + assert_eq!(d.to_string(), "-1234567.89"); + + // Test trailing zeros are trimmed + let d = VariantDecimal8::try_new(123000000, 6).unwrap(); + assert_eq!(d.to_string(), "123"); + + let d = VariantDecimal8::try_new(-123000000, 6).unwrap(); + assert_eq!(d.to_string(), "-123"); + + // Test leading zeros in decimal part + let d = VariantDecimal8::try_new(100005, 6).unwrap(); + assert_eq!(d.to_string(), "0.100005"); + + let d = VariantDecimal8::try_new(-100005, 6).unwrap(); + assert_eq!(d.to_string(), "-0.100005"); + + // Test number smaller than scale + let d = VariantDecimal8::try_new(123, 10).unwrap(); + assert_eq!(d.to_string(), "0.0000000123"); + + let d = VariantDecimal8::try_new(-123, 10).unwrap(); + assert_eq!(d.to_string(), "-0.0000000123"); + + // Test zero + let d = VariantDecimal8::try_new(0, 0).unwrap(); + assert_eq!(d.to_string(), "0"); + + let d = VariantDecimal8::try_new(0, 10).unwrap(); + assert_eq!(d.to_string(), "0"); + + // Test max scale + let d = VariantDecimal8::try_new(123456789012345678, 18).unwrap(); + assert_eq!(d.to_string(), "0.123456789012345678"); + + let d = VariantDecimal8::try_new(-123456789012345678, 18).unwrap(); + assert_eq!(d.to_string(), "-0.123456789012345678"); + + // Test max precision + let d = VariantDecimal8::try_new(999999999999999999, 0).unwrap(); + assert_eq!(d.to_string(), "999999999999999999"); + + let d = VariantDecimal8::try_new(-999999999999999999, 0).unwrap(); + assert_eq!(d.to_string(), "-999999999999999999"); + + // Test complex trailing zeros + let d = VariantDecimal8::try_new(1200000050000, 10).unwrap(); + assert_eq!(d.to_string(), "120.000005"); + + let d = VariantDecimal8::try_new(-1200000050000, 10).unwrap(); + assert_eq!(d.to_string(), "-120.000005"); + } + + #[test] + fn test_variant_decimal16_display() { + // Test zero scale (integers) + let d = VariantDecimal16::try_new(42, 0).unwrap(); + assert_eq!(d.to_string(), "42"); + + let d = VariantDecimal16::try_new(-42, 0).unwrap(); + assert_eq!(d.to_string(), "-42"); + + // Test basic decimal formatting + let d = VariantDecimal16::try_new(123456789012345, 4).unwrap(); + assert_eq!(d.to_string(), "12345678901.2345"); + + let d = VariantDecimal16::try_new(-123456789012345, 4).unwrap(); + assert_eq!(d.to_string(), "-12345678901.2345"); + + // Test trailing zeros are trimmed + let d = VariantDecimal16::try_new(12300000000, 8).unwrap(); + assert_eq!(d.to_string(), "123"); + + let d = VariantDecimal16::try_new(-12300000000, 8).unwrap(); + assert_eq!(d.to_string(), "-123"); + + // Test leading zeros in decimal part + let d = VariantDecimal16::try_new(10000005, 8).unwrap(); + assert_eq!(d.to_string(), "0.10000005"); + + let d = VariantDecimal16::try_new(-10000005, 8).unwrap(); + assert_eq!(d.to_string(), "-0.10000005"); + + // Test number smaller than scale + let d = VariantDecimal16::try_new(123, 20).unwrap(); + assert_eq!(d.to_string(), "0.00000000000000000123"); + + let d = VariantDecimal16::try_new(-123, 20).unwrap(); + assert_eq!(d.to_string(), "-0.00000000000000000123"); + + // Test zero + let d = VariantDecimal16::try_new(0, 0).unwrap(); + assert_eq!(d.to_string(), "0"); + + let d = VariantDecimal16::try_new(0, 20).unwrap(); + assert_eq!(d.to_string(), "0"); + + // Test max scale + let d = VariantDecimal16::try_new(12345678901234567890123456789012345678_i128, 38).unwrap(); + assert_eq!(d.to_string(), "0.12345678901234567890123456789012345678"); + + let d = + VariantDecimal16::try_new(-12345678901234567890123456789012345678_i128, 38).unwrap(); + assert_eq!(d.to_string(), "-0.12345678901234567890123456789012345678"); + + // Test max precision integer + let d = VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 0).unwrap(); + assert_eq!(d.to_string(), "99999999999999999999999999999999999999"); + + let d = VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 0).unwrap(); + assert_eq!(d.to_string(), "-99999999999999999999999999999999999999"); + + // Test complex trailing zeros + let d = VariantDecimal16::try_new(12000000000000050000000000000_i128, 25).unwrap(); + assert_eq!(d.to_string(), "1200.000000000005"); + + let d = VariantDecimal16::try_new(-12000000000000050000000000000_i128, 25).unwrap(); + assert_eq!(d.to_string(), "-1200.000000000005"); + + // Test large integer that would overflow i64 but fits in i128 + let large_int = 12345678901234567890123456789_i128; + let d = VariantDecimal16::try_new(large_int, 0).unwrap(); + assert_eq!(d.to_string(), "12345678901234567890123456789"); + + let d = VariantDecimal16::try_new(-large_int, 0).unwrap(); + assert_eq!(d.to_string(), "-12345678901234567890123456789"); + } } diff --git a/parquet-variant/src/variant/metadata.rs b/parquet-variant/src/variant/metadata.rs index 8fff65a6ee8f..16b4df6f3f12 100644 --- a/parquet-variant/src/variant/metadata.rs +++ b/parquet-variant/src/variant/metadata.rs @@ -60,8 +60,7 @@ impl VariantMetadataHeader { let version = header_byte & 0x0F; // First four bits if version != CORRECT_VERSION_VALUE { let err_msg = format!( - "The version bytes in the header is not {CORRECT_VERSION_VALUE}, got {:b}", - version + "The version bytes in the header is not {CORRECT_VERSION_VALUE}, got {version:b}", ); return Err(ArrowError::InvalidArgumentError(err_msg)); }