Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decimal type related functionality is missing #122

Closed
gszabo opened this issue Jul 9, 2021 · 7 comments
Closed

Decimal type related functionality is missing #122

gszabo opened this issue Jul 9, 2021 · 7 comments

Comments

@gszabo
Copy link

gszabo commented Jul 9, 2021

I noticed the Decimal type related functionality of the driver is not exported in this crate. For example the bind_decimal method is commented out: https://github.com/Metaswitch/cassandra-rs/blob/master/src/cassandra/statement.rs#L682

What is the reason behind that? Is there a workaround clients can use? What would need to be solved so this crate can expose that functionality?

@kw217
Copy link
Collaborator

kw217 commented Aug 6, 2021

Thanks @gszabo and apologies for slow reply. This is simply a gap - we'd welcome a PR that added it. Please feel free to propose a design here and we can advise before you start implementing - I guess the key question is what Rust type to use. The functionality is present in the underlying cassandra-sys-rs crate, so it's just this crate that needs to be updated.

@M5135213521asd3
Copy link
Contributor

M5135213521asd3 commented Nov 28, 2023

I was playing around with this a little bit. Cpp driver provide 3 values:
varint - address
varint_size - number of bytes to read from varint
scale - scale value

as an example, number 6.31:
3 bytes from varint: [0, 2, 119] => 631 as int
scale: 2
631/(10^2)

This is fine, now tricky part. Cassandra can return more that 16 bytes which doesn't fit to i128. For example, this is valid cassandra decimal value:

size:17, scale:31
bytes: [0, 13, 122, 129, 44, 68, 99, 173, 234, 226, 22, 226, 101, 176, 138, 84, 65]
decimal number:
1791604.4099988662637770175933837890625

Deep dive to cql docu follow me to java type BigDecimal (https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html)
arbitrary-precision signed decimal numbers. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale.

I was looking for some implementation of bigdecimal crates, but I didn't found anything satisfying.

Here is my implementation of get_dec(), but I have limit to max number which can be saved to DB.


#[derive(Debug)]
pub struct Decimal {
    pub value: i32,
    pub scale: i32
}

pub fn get_decimal(&self) -> Result<Decimal> {
            let mut varint = std::ptr::null();
            let mut varint_size= 0;
            let mut scale= 0;
            let mut decimal_bytes: [u8; 4] = [0,0,0,0];

            unsafe{
                // get decimal values from c driver
                if cass_value_get_decimal(self.0, &mut varint, &mut varint_size, &mut scale) != CASS_OK {
                    return Err(CASS_ERROR_LIB_INVALID_VALUE_TYPE.to_error());
                }

                // check if bytes fits to i32
                if varint_size > 4 {
                    return Err(CASS_ERROR_LIB_INVALID_VALUE_TYPE.to_error());
                }

                // read varint_size bytes from varint and convert to vec
                let var_bytes: Vec<u8> = std::slice::from_raw_parts( varint, varint_size).to_vec();
              
                // fill decimal_bytes with var_bytes on shifted position base on varint_size
                let mut current_byte_loc = 0;
                for n in (4-varint_size)..4 {
                    decimal_bytes[n] = var_bytes[current_byte_loc];
                    current_byte_loc = current_byte_loc + 1;
                }
            }
            
            // convert decimal_bytes to i32
            let value: i32 = i32::from_be_bytes(decimal_bytes);

            // create decimal struct
            let decimal = Decimal {
                value: value,
                scale: scale,
            };

            return Ok(decimal);
            
    }

@kw217
Copy link
Collaborator

kw217 commented Dec 1, 2023

Thanks. I'd rather we didn't roll our own bignum type. A quick search on crates.io turned up two likely options:

There were others, but they looked less well maintained or used. (I may have missed some though.) There's an interesting table here https://github.com/rust-num/num-bigint#alternatives but many of those are just big integer libraries; they don't do decimals. There's also num-rational, but that doesn't have any special handling for decimals, so (e.g.) printing wouldn't come out right.

I prefer to avoid additional dependencies, so prefer https://crates.io/crates/bigdecimal . What do you think?

@M5135213521asd3
Copy link
Contributor

This pass my tests, but more tests are required...

    pub fn get_decimal(&self) -> Result<BigDecimal> {
        let mut varint = std::ptr::null();
        let mut varint_size= 0;
        let mut scale= 0;
        
        let slice = unsafe{
            // get decimal values from c driver
            if cass_value_get_decimal(self.0, &mut varint, &mut varint_size, &mut scale) != cassandra_cpp_sys::CassError::CASS_OK {
                return Err(CASS_ERROR_LIB_INVALID_VALUE_TYPE.to_error());
            }

            slice::from_raw_parts(varint, varint_size)
        };
        
        let bigint = BigInt::from_signed_bytes_be(slice);
        let bigdec = BigDecimal::new(bigint, scale as i64);
       
        return Ok(bigdec);
    }

@kw217
Copy link
Collaborator

kw217 commented Dec 15, 2023

Thanks - that looks perfectly fine to me :-)

@M5135213521asd3
Copy link
Contributor

this can be closed

@kw217
Copy link
Collaborator

kw217 commented Mar 21, 2024

Thanks for flagging! Indeed, fixed in #177 .

@kw217 kw217 closed this as completed Mar 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants