Skip to content

Commit

Permalink
ARROW-10818: [Rust] Implement DecimalType
Browse files Browse the repository at this point in the history
  • Loading branch information
ovr committed Jan 17, 2021
1 parent e73f205 commit 762bd31
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 13 deletions.
1 change: 1 addition & 0 deletions rust/arrow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ flatbuffers = "^0.8"
hex = "0.4"
prettytable-rs = { version = "0.8.0", optional = true }
lexical-core = "^0.7"
num-bigint = "0.3"

[features]
default = []
Expand Down
18 changes: 8 additions & 10 deletions rust/arrow/src/array/array_binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use super::{
};
use crate::util::bit_util;
use crate::{buffer::Buffer, datatypes::ToByteSlice};
use crate::{buffer::MutableBuffer, datatypes::DataType};
use crate::{buffer::MutableBuffer, datatypes::DataType, datatypes::DecimalType};

/// Like OffsetSizeTrait, but specialized for Binary
// This allow us to expose a constant datatype for the GenericBinaryArray
Expand Down Expand Up @@ -485,9 +485,9 @@ pub struct DecimalArray {
}

impl DecimalArray {
/// Returns the element at index `i` as i128.
pub fn value(&self, i: usize) -> i128 {
pub fn value(&self, i: usize) -> DecimalType {
assert!(i < self.data.len(), "DecimalArray out of bounds access");

let offset = i.checked_add(self.data.offset()).unwrap();
let raw_val = unsafe {
let pos = self.value_offset_at(offset);
Expand All @@ -496,11 +496,8 @@ impl DecimalArray {
(self.value_offset_at(offset + 1) - pos) as usize,
)
};
let as_array = raw_val.try_into();
match as_array {
Ok(v) if raw_val.len() == 16 => i128::from_le_bytes(v),
_ => panic!("DecimalArray elements are not 128bit integers."),
}

DecimalType::from_bytes(raw_val, self.scale, self.precision)
}

/// Returns the offset for the element at index `i`.
Expand Down Expand Up @@ -992,8 +989,9 @@ mod tests {
.add_buffer(Buffer::from(&values[..]))
.build();
let decimal_array = DecimalArray::from(array_data);
assert_eq!(8_887_000_000, decimal_array.value(0));
assert_eq!(-8_887_000_000, decimal_array.value(1));

assert_eq!(DecimalType::from(8_887_000_000_i128), decimal_array.value(0));
assert_eq!(DecimalType::from(-8_887_000_000_i128), decimal_array.value(1));
assert_eq!(16, decimal_array.value_length());
}

Expand Down
2 changes: 1 addition & 1 deletion rust/arrow/src/array/equal_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ impl JsonEqual for DecimalArray {
JString(s) => {
self.is_valid(i)
&& (s
.parse::<i128>()
.parse::<DecimalType>()
.map_or_else(|_| false, |v| v == self.value(i)))
}
JNull => self.is_null(i),
Expand Down
162 changes: 162 additions & 0 deletions rust/arrow/src/datatypes/decimal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

use std::{convert::TryInto, fmt, ops::Add, str::FromStr};

use num::{BigInt, Zero};

// Decimal (Numeric) implementation on top of BigInt, it's a common implementation to support all scale/precision
// Decimal (scale, precision) = scale.precision = Decimal(1, 2) = 1.00
// @todo Implementation of Int32/64 can be implemented soon for performance reason
#[derive(Clone, Eq)]
pub struct DecimalType {
digits: BigInt,
scale: usize,
precision: usize,
}

impl DecimalType {
pub fn new(digits: BigInt, scale: usize, precision: usize) -> DecimalType {
DecimalType {
digits,
scale,
precision,
}
}

pub fn from_bytes(bytes: &[u8], scale: usize, precision: usize) -> DecimalType {
let as_array = bytes.try_into();
match as_array {
Ok(v) if bytes.len() == 16 => DecimalType {
digits: BigInt::from(i128::from_le_bytes(v)),
scale,
precision,
},
_ => panic!("Unsupported Decimal"),
}
}

#[inline(always)]
fn rescale(mut self, new_scale: usize, new_precision: usize) -> DecimalType {
if self.digits.is_zero() {
return DecimalType::new(BigInt::zero(), new_scale, new_precision);
}

unimplemented!();
}
}

impl Default for DecimalType {
#[inline]
fn default() -> DecimalType {
Zero::zero()
}
}

impl Zero for DecimalType {
#[inline]
fn zero() -> DecimalType {
DecimalType::new(BigInt::zero(), 0, 0)
}

#[inline]
fn is_zero(&self) -> bool {
self.digits.is_zero()
}
}

impl Add<DecimalType> for DecimalType {
type Output = DecimalType;

#[inline]
fn add(self, rhs: DecimalType) -> DecimalType {
let mut lhs = self;

unimplemented!()
}
}

impl PartialEq<DecimalType> for DecimalType {
#[inline]
fn eq(&self, rhs: &DecimalType) -> bool {
if self.precision > rhs.precision {
unimplemented!()
} else if self.precision < rhs.precision {
unimplemented!()
} else {
self.digits == rhs.digits
}
}
}

impl fmt::Display for DecimalType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unimplemented!()
}
}

impl fmt::Debug for DecimalType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unimplemented!()
}
}

macro_rules! impl_from {
($TYPE:tt) => {
impl From<$TYPE> for DecimalType {
#[inline]
fn from(n: $TYPE) -> Self {
DecimalType {
digits: BigInt::from(n),
scale: 0,
precision: 0,
}
}
}
};
}

impl_from!(i128);
impl_from!(i64);
impl_from!(i32);
impl_from!(u128);
impl_from!(u64);
impl_from!(u32);

#[derive(Debug, PartialEq)]
pub enum ParseDecimalError {
Empty,
Other(String),
}

impl FromStr for DecimalType {
type Err = ParseDecimalError;

fn from_str(s: &str) -> Result<Self, ParseDecimalError> {
unimplemented!()
}
}

mod test {
#[test]
fn test_from_bytes() {
let values: [u8; 32] = [
192, 219, 180, 17, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 36, 75, 238, 253,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ use serde_json::{

use crate::error::{ArrowError, Result};

#[allow(clippy::module_inception)]
mod decimal;

pub use self::decimal::DecimalType;

/// The set of datatypes that are supported by this implementation of Apache Arrow.
///
/// The Arrow specification on data types includes some more types.
Expand Down
4 changes: 2 additions & 2 deletions rust/parquet/src/arrow/arrow_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ mod tests {
use crate::schema::parser::parse_message_type;
use crate::schema::types::TypePtr;
use crate::util::test_common::{get_temp_filename, RandGen};
use arrow::array::*;
use arrow::{array::*, datatypes::DecimalType};
use arrow::record_batch::RecordBatchReader;
use rand::RngCore;
use serde_json::json;
Expand Down Expand Up @@ -429,7 +429,7 @@ mod tests {
assert_eq!(col.scale(), 2);

for (i, v) in expected.enumerate() {
assert_eq!(col.value(i), v * 100_i128);
assert_eq!(col.value(i), DecimalType::from(v * 100_i128));
}
}
}
Expand Down

0 comments on commit 762bd31

Please sign in to comment.