diff --git a/Cargo.toml b/Cargo.toml index cfac622..47e0752 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ combine = "^4.6" percent-encoding = "^2.3" either = "^1.6" cfg-if = "1.0.0" +ethnum = "^1.5.0" [dependencies.futures-util] version = "^0.3" diff --git a/README.md b/README.md index eedb4e4..371e8cd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ clickhouse-rs = "*" * Decimal(P, S) * Float32, Float64 * String, FixedString(N) -* UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, Int128 +* UInt8, UInt16, UInt32, UInt64, UInt128, UInt256, Int8, Int16, Int32, Int64, Int128, UInt256 * Nullable(T) * Array(UInt/Int/Float/String/Date/DateTime) * SimpleAggregateFunction(F, T) diff --git a/src/types/block/mod.rs b/src/types/block/mod.rs index 4ab5446..f96d7bb 100644 --- a/src/types/block/mod.rs +++ b/src/types/block/mod.rs @@ -10,6 +10,7 @@ use std::{ use byteorder::{LittleEndian, WriteBytesExt}; use chrono_tz::Tz; use clickhouse_rs_cityhash_sys::city_hash_128; +use ethnum::{i256, u256}; use lz4::liblz4::{LZ4_compressBound, LZ4_compress_default}; use crate::{ @@ -64,12 +65,14 @@ sliceable! { u32: UInt32, u64: UInt64, u128: UInt128, + u256: UInt256, i8: Int8, i16: Int16, i32: Int32, i64: Int64, - i128: Int128 + i128: Int128, + i256: Int256 } /// Represents Clickhouse Block diff --git a/src/types/column/factory.rs b/src/types/column/factory.rs index b04c45b..12fcc63 100644 --- a/src/types/column/factory.rs +++ b/src/types/column/factory.rs @@ -1,4 +1,5 @@ use chrono_tz::Tz; +use ethnum::{i256, u256}; use combine::{ any, choice, @@ -67,11 +68,13 @@ impl dyn ColumnData { "UInt32" => W::wrap(VectorColumnData::::load(reader, size)?), "UInt64" => W::wrap(VectorColumnData::::load(reader, size)?), "UInt128" => W::wrap(VectorColumnData::::load(reader, size)?), + "UInt256" => W::wrap(VectorColumnData::::load(reader, size)?), "Int8" | "TinyInt" => W::wrap(VectorColumnData::::load(reader, size)?), "Int16" | "SmallInt" => W::wrap(VectorColumnData::::load(reader, size)?), "Int32" | "Int" | "Integer" => W::wrap(VectorColumnData::::load(reader, size)?), "Int64" | "BigInt" => W::wrap(VectorColumnData::::load(reader, size)?), "Int128" => W::wrap(VectorColumnData::::load(reader, size)?), + "Int256" => W::wrap(VectorColumnData::::load(reader, size)?), "Float32" | "Float" => W::wrap(VectorColumnData::::load(reader, size)?), "Float64" | "Double" => W::wrap(VectorColumnData::::load(reader, size)?), "String" | "Char" | "Varchar" | "Text" | "TinyText" | "MediumText" | "LongText" | "Blob" | "TinyBlob" | "MediumBlob" | "LongBlob" => W::wrap(StringColumnData::load(reader, size)?), @@ -124,11 +127,13 @@ impl dyn ColumnData { SqlType::UInt32 => W::wrap(VectorColumnData::::with_capacity(capacity)), SqlType::UInt64 => W::wrap(VectorColumnData::::with_capacity(capacity)), SqlType::UInt128 => W::wrap(VectorColumnData::::with_capacity(capacity)), + SqlType::UInt256 => W::wrap(VectorColumnData::::with_capacity(capacity)), SqlType::Int8 => W::wrap(VectorColumnData::::with_capacity(capacity)), SqlType::Int16 => W::wrap(VectorColumnData::::with_capacity(capacity)), SqlType::Int32 => W::wrap(VectorColumnData::::with_capacity(capacity)), SqlType::Int64 => W::wrap(VectorColumnData::::with_capacity(capacity)), SqlType::Int128 => W::wrap(VectorColumnData::::with_capacity(capacity)), + SqlType::Int256 => W::wrap(VectorColumnData::::with_capacity(capacity)), SqlType::String => W::wrap(StringColumnData::with_capacity(capacity)), SqlType::FixedString(len) => { W::wrap(FixedStringColumnData::with_capacity(capacity, len)) diff --git a/src/types/column/iter/mod.rs b/src/types/column/iter/mod.rs index b3ac4a9..0851a49 100644 --- a/src/types/column/iter/mod.rs +++ b/src/types/column/iter/mod.rs @@ -2,6 +2,7 @@ use chrono::prelude::*; use chrono_tz::Tz; +use ethnum::{i256, u256}; use std::{ collections::HashMap, hash::Hash, @@ -78,7 +79,10 @@ simple_num_iterable! { f64: Float64, i128: Int128, - u128: UInt128 + u128: UInt128, + + i256: Int256, + u256: UInt256 } macro_rules! iterator { diff --git a/src/types/column/numeric.rs b/src/types/column/numeric.rs index b422408..7b0d51c 100644 --- a/src/types/column/numeric.rs +++ b/src/types/column/numeric.rs @@ -205,12 +205,14 @@ where Value::UInt32(x) => ValueRef::UInt32(x), Value::UInt64(x) => ValueRef::UInt64(x), Value::UInt128(x) => ValueRef::UInt128(x), + Value::UInt256(x) => ValueRef::UInt256(x), Value::Int8(x) => ValueRef::Int8(x), Value::Int16(x) => ValueRef::Int16(x), Value::Int32(x) => ValueRef::Int32(x), Value::Int64(x) => ValueRef::Int64(x), Value::Int128(x) => ValueRef::Int128(x), + Value::Int256(x) => ValueRef::Int256(x), Value::Float32(x) => ValueRef::Float32(x), Value::Float64(x) => ValueRef::Float64(x), diff --git a/src/types/from_sql.rs b/src/types/from_sql.rs index 6aaefb5..147a5f0 100644 --- a/src/types/from_sql.rs +++ b/src/types/from_sql.rs @@ -1,6 +1,7 @@ use chrono::{prelude::*, Duration}; use chrono_tz::Tz; use either::Either; +use ethnum::{i256, u256}; use std::collections::HashMap; use std::hash::Hash; use std::net::{Ipv4Addr, Ipv6Addr}; @@ -262,11 +263,13 @@ from_sql_vec_impl! { i32: Int32, i64: Int64, i128: Int128, + i256: Int256, u16: UInt16, u32: UInt32, u64: UInt64, u128: UInt128, + u256: UInt256, f32: Float32, f64: Float64 @@ -343,12 +346,14 @@ from_sql_impl! { u32: UInt32, u64: UInt64, u128: UInt128, + u256: UInt256, i8: Int8, i16: Int16, i32: Int32, i64: Int64, i128: Int128, + i256: Int256, f32: Float32, f64: Float64 diff --git a/src/types/marshal.rs b/src/types/marshal.rs index 4acd863..ff62514 100644 --- a/src/types/marshal.rs +++ b/src/types/marshal.rs @@ -1,3 +1,5 @@ +use ethnum::{i256, u256}; + pub trait Marshal { fn marshal(&self, scratch: &mut [u8]); } @@ -62,6 +64,13 @@ impl Marshal for u128 { } } +impl Marshal for u256 { + fn marshal(&self, scratch: &mut [u8]) { + self.low().marshal(&mut scratch[0..]); + self.high().marshal(&mut scratch[16..]); + } +} + impl Marshal for i8 { fn marshal(&self, scratch: &mut [u8]) { scratch[0] = *self as u8; @@ -122,6 +131,13 @@ impl Marshal for i128 { } } +impl Marshal for i256 { + fn marshal(&self, scratch: &mut [u8]) { + self.low().marshal(&mut scratch[0..]); + self.high().marshal(&mut scratch[16..]); + } +} + impl Marshal for f32 { fn marshal(&self, scratch: &mut [u8]) { let bits = self.to_bits(); @@ -157,6 +173,7 @@ mod test { use std::fmt; use crate::types::{Marshal, StatBuffer, Unmarshal}; + use ethnum::{i256, u256}; use rand::distributions::{Distribution, Standard}; use rand::random; @@ -201,6 +218,21 @@ mod test { test_some::() } + #[test] + fn test_u256() { + for _ in 0..100 { + let mut buffer = u256::buffer(); + let v1 = random::(); + let v2 = random::(); + let v = u256::from_words(v1, v2); + + v.marshal(buffer.as_mut()); + let u = u256::unmarshal(buffer.as_ref()); + + assert_eq!(v, u); + } + } + #[test] fn test_i8() { test_some::() @@ -226,6 +258,21 @@ mod test { test_some::() } + #[test] + fn test_i256() { + for _ in 0..100 { + let mut buffer = i256::buffer(); + let v1 = random::(); + let v2 = random::(); + let v = i256::from_words(v1, v2); + + v.marshal(buffer.as_mut()); + let u = i256::unmarshal(buffer.as_ref()); + + assert_eq!(v, u); + } + } + #[test] fn test_f32() { test_some::() diff --git a/src/types/mod.rs b/src/types/mod.rs index 734dea0..f97e8b5 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -2,6 +2,7 @@ use std::{borrow::Cow, collections::HashMap, fmt, mem, pin::Pin, str::FromStr, s use chrono::prelude::*; use chrono_tz::Tz; +use ethnum::{i256, u256}; use hostname::get; use lazy_static::lazy_static; @@ -207,11 +208,13 @@ has_sql_type! { u32: SqlType::UInt32, u64: SqlType::UInt64, u128: SqlType::UInt128, + u256: SqlType::UInt256, i8: SqlType::Int8, i16: SqlType::Int16, i32: SqlType::Int32, i64: SqlType::Int64, i128: SqlType::Int128, + i256: SqlType::Int256, &str: SqlType::String, String: SqlType::String, f32: SqlType::Float32, @@ -314,11 +317,13 @@ pub enum SqlType { UInt32, UInt64, UInt128, + UInt256, Int8, Int16, Int32, Int64, Int128, + Int256, String, FixedString(usize), Float32, @@ -382,11 +387,13 @@ impl SqlType { SqlType::UInt32 => "UInt32".into(), SqlType::UInt64 => "UInt64".into(), SqlType::UInt128 => "UInt128".into(), + SqlType::UInt256 => "UInt256".into(), SqlType::Int8 => "Int8".into(), SqlType::Int16 => "Int16".into(), SqlType::Int32 => "Int32".into(), SqlType::Int64 => "Int64".into(), SqlType::Int128 => "Int128".into(), + SqlType::Int256 => "Int256".into(), SqlType::String => "String".into(), SqlType::FixedString(str_len) => format!("FixedString({str_len})").into(), SqlType::Float32 => "Float32".into(), diff --git a/src/types/stat_buffer.rs b/src/types/stat_buffer.rs index 7e5dae8..94548eb 100644 --- a/src/types/stat_buffer.rs +++ b/src/types/stat_buffer.rs @@ -1,4 +1,5 @@ use crate::types::SqlType; +use ethnum::{i256, u256}; pub trait StatBuffer { type Buffer: AsMut<[u8]> + AsRef<[u8]> + Copy + Sync; @@ -66,6 +67,18 @@ impl StatBuffer for u128 { } } +impl StatBuffer for u256 { + type Buffer = [u8; 32]; + + fn buffer() -> Self::Buffer { + [0; 32] + } + + fn sql_type() -> SqlType { + SqlType::UInt256 + } +} + impl StatBuffer for i8 { type Buffer = [u8; 1]; @@ -126,6 +139,18 @@ impl StatBuffer for i128 { } } +impl StatBuffer for i256 { + type Buffer = [u8; 32]; + + fn buffer() -> Self::Buffer { + [0; 32] + } + + fn sql_type() -> SqlType { + SqlType::Int256 + } +} + impl StatBuffer for f32 { type Buffer = [u8; 4]; diff --git a/src/types/unmarshal.rs b/src/types/unmarshal.rs index ec14ef3..0fb0368 100644 --- a/src/types/unmarshal.rs +++ b/src/types/unmarshal.rs @@ -1,3 +1,5 @@ +use ethnum::{i256, u256}; + pub trait Unmarshal { fn unmarshal(scratch: &[u8]) -> T; } @@ -57,6 +59,15 @@ impl Unmarshal for u128 { } } +impl Unmarshal for u256 { + fn unmarshal(scratch: &[u8]) -> Self { + Self::from_words( + u128::unmarshal(&scratch[16..]), + u128::unmarshal(&scratch[0..]), + ) + } +} + impl Unmarshal for i8 { fn unmarshal(scratch: &[u8]) -> Self { scratch[0] as Self @@ -112,6 +123,15 @@ impl Unmarshal for i128 { } } +impl Unmarshal for i256 { + fn unmarshal(scratch: &[u8]) -> Self { + Self::from_words( + i128::unmarshal(&scratch[16..]), + i128::unmarshal(&scratch[0..]), + ) + } +} + impl Unmarshal for f32 { fn unmarshal(scratch: &[u8]) -> Self { let bits = u32::from(scratch[0]) diff --git a/src/types/value.rs b/src/types/value.rs index cd147ce..6089782 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -11,6 +11,7 @@ use std::{ use chrono::{prelude::*, Duration}; use chrono_tz::Tz; use either::Either; +use ethnum::{i256, u256}; use uuid::Uuid; use crate::types::{ @@ -31,11 +32,13 @@ pub enum Value { UInt32(u32), UInt64(u64), UInt128(u128), + UInt256(u256), Int8(i8), Int16(i16), Int32(i32), Int64(i64), Int128(i128), + Int256(i256), String(Arc>), Float32(f32), Float64(f64), @@ -67,11 +70,13 @@ impl Hash for Value { Self::Int32(i) => i.hash(state), Self::Int64(i) => i.hash(state), Self::Int128(i) => i.hash(state), + Self::Int256(i) => i.hash(state), Self::UInt8(i) => i.hash(state), Self::UInt16(i) => i.hash(state), Self::UInt32(i) => i.hash(state), Self::UInt64(i) => i.hash(state), Self::UInt128(i) => i.hash(state), + Self::UInt256(i) => i.hash(state), _ => unimplemented!(), } } @@ -88,11 +93,13 @@ impl PartialEq for Value { (Value::UInt32(a), Value::UInt32(b)) => *a == *b, (Value::UInt64(a), Value::UInt64(b)) => *a == *b, (Value::UInt128(a), Value::UInt128(b)) => *a == *b, + (Value::UInt256(a), Value::UInt256(b)) => a.into_words() == b.into_words(), (Value::Int8(a), Value::Int8(b)) => *a == *b, (Value::Int16(a), Value::Int16(b)) => *a == *b, (Value::Int32(a), Value::Int32(b)) => *a == *b, (Value::Int64(a), Value::Int64(b)) => *a == *b, (Value::Int128(a), Value::Int128(b)) => *a == *b, + (Value::Int256(a), Value::Int256(b)) => a.into_words() == b.into_words(), (Value::String(a), Value::String(b)) => *a == *b, (Value::Float32(a), Value::Float32(b)) => *a == *b, (Value::Float64(a), Value::Float64(b)) => *a == *b, @@ -154,11 +161,13 @@ impl Value { SqlType::UInt32 => Value::UInt32(0), SqlType::UInt64 => Value::UInt64(0), SqlType::UInt128 => Value::UInt128(0), + SqlType::UInt256 => Value::UInt256(u256::new(0)), SqlType::Int8 => Value::Int8(0), SqlType::Int16 => Value::Int16(0), SqlType::Int32 => Value::Int32(0), SqlType::Int64 => Value::Int64(0), SqlType::Int128 => Value::Int128(0), + SqlType::Int256 => Value::Int256(i256::new(0)), SqlType::String => Value::String(Arc::new(Vec::default())), SqlType::FixedString(str_len) => Value::String(Arc::new(vec![0_u8; str_len])), SqlType::Float32 => Value::Float32(0.0), @@ -196,11 +205,13 @@ impl fmt::Display for Value { Value::UInt32(ref v) => fmt::Display::fmt(v, f), Value::UInt64(ref v) => fmt::Display::fmt(v, f), Value::UInt128(ref v) => fmt::Display::fmt(v, f), + Value::UInt256(ref v) => fmt::Display::fmt(v, f), Value::Int8(ref v) => fmt::Display::fmt(v, f), Value::Int16(ref v) => fmt::Display::fmt(v, f), Value::Int32(ref v) => fmt::Display::fmt(v, f), Value::Int64(ref v) => fmt::Display::fmt(v, f), Value::Int128(ref v) => fmt::Display::fmt(v, f), + Value::Int256(ref v) => fmt::Display::fmt(v, f), Value::String(ref v) => match str::from_utf8(v) { Ok(s) => fmt::Display::fmt(s, f), Err(_) => write!(f, "{v:?}"), @@ -281,11 +292,13 @@ impl From for SqlType { Value::UInt32(_) => SqlType::UInt32, Value::UInt64(_) => SqlType::UInt64, Value::UInt128(_) => SqlType::UInt128, + Value::UInt256(_) => SqlType::UInt256, Value::Int8(_) => SqlType::Int8, Value::Int16(_) => SqlType::Int16, Value::Int32(_) => SqlType::Int32, Value::Int64(_) => SqlType::Int64, Value::Int128(_) => SqlType::Int128, + Value::Int256(_) => SqlType::Int256, Value::String(_) => SqlType::String, Value::Float32(_) => SqlType::Float32, Value::Float64(_) => SqlType::Float64, @@ -450,12 +463,14 @@ value_from! { u32: UInt32, u64: UInt64, u128: UInt128, + u256: UInt256, i8: Int8, i16: Int16, i32: Int32, i64: Int64, i128: Int128, + i256: Int256, f32: Float32, f64: Float64, @@ -471,12 +486,14 @@ value_array_from! { u32: UInt32, u64: UInt64, u128: UInt128, + u256: UInt256, i8: Int8, i16: Int16, i32: Int32, i64: Int64, i128: Int128, + i256: Int256, f32: Float32, f64: Float64 @@ -567,11 +584,13 @@ from_value! { u32: UInt32, u64: UInt64, u128: UInt128, + u256: UInt256, i8: Int8, i16: Int16, i32: Int32, i64: Int64, i128: Int128, + i256: Int256, f32: Float32, f64: Float64, [u8; 4]: Ipv4 @@ -736,12 +755,20 @@ mod test { assert_eq!("42".to_string(), format!("{}", Value::UInt32(42))); assert_eq!("42".to_string(), format!("{}", Value::UInt64(42))); assert_eq!("42".to_string(), format!("{}", Value::UInt128(42))); + assert_eq!( + "42".to_string(), + format!("{}", Value::UInt256(u256::new(42))) + ); assert_eq!("42".to_string(), format!("{}", Value::Int8(42))); assert_eq!("42".to_string(), format!("{}", Value::Int16(42))); assert_eq!("42".to_string(), format!("{}", Value::Int32(42))); assert_eq!("42".to_string(), format!("{}", Value::Int64(42))); assert_eq!("42".to_string(), format!("{}", Value::Int128(42))); + assert_eq!( + "42".to_string(), + format!("{}", Value::Int256(i256::new(42))) + ); assert_eq!( "text".to_string(), diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 081c0b2..8c3b22b 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -9,6 +9,7 @@ use std::{ use chrono::{prelude::*, Duration}; use chrono_tz::Tz; use either::Either; +use ethnum::{i256, u256}; use uuid::Uuid; use crate::{ @@ -29,11 +30,13 @@ pub enum ValueRef<'a> { UInt32(u32), UInt64(u64), UInt128(u128), + UInt256(u256), Int8(i8), Int16(i16), Int32(i32), Int64(i64), Int128(i128), + Int256(i256), String(&'a [u8]), Float32(f32), Float64(f64), @@ -64,11 +67,13 @@ impl<'a> Hash for ValueRef<'a> { Self::Int32(i) => i.hash(state), Self::Int64(i) => i.hash(state), Self::Int128(i) => i.hash(state), + Self::Int256(i) => i.hash(state), Self::UInt8(i) => i.hash(state), Self::UInt16(i) => i.hash(state), Self::UInt32(i) => i.hash(state), Self::UInt64(i) => i.hash(state), Self::UInt128(i) => i.hash(state), + Self::UInt256(i) => i.hash(state), _ => unimplemented!(), } } @@ -84,11 +89,13 @@ impl<'a> PartialEq for ValueRef<'a> { (ValueRef::UInt32(a), ValueRef::UInt32(b)) => *a == *b, (ValueRef::UInt64(a), ValueRef::UInt64(b)) => *a == *b, (ValueRef::UInt128(a), ValueRef::UInt128(b)) => *a == *b, + (ValueRef::UInt256(a), ValueRef::UInt256(b)) => a.into_words() == b.into_words(), (ValueRef::Int8(a), ValueRef::Int8(b)) => *a == *b, (ValueRef::Int16(a), ValueRef::Int16(b)) => *a == *b, (ValueRef::Int32(a), ValueRef::Int32(b)) => *a == *b, (ValueRef::Int64(a), ValueRef::Int64(b)) => *a == *b, (ValueRef::Int128(a), ValueRef::Int128(b)) => *a == *b, + (ValueRef::Int256(a), ValueRef::Int256(b)) => a.into_words() == b.into_words(), (ValueRef::String(a), ValueRef::String(b)) => *a == *b, (ValueRef::Float32(a), ValueRef::Float32(b)) => *a == *b, (ValueRef::Float64(a), ValueRef::Float64(b)) => *a == *b, @@ -132,11 +139,13 @@ impl<'a> fmt::Display for ValueRef<'a> { ValueRef::UInt32(v) => fmt::Display::fmt(v, f), ValueRef::UInt64(v) => fmt::Display::fmt(v, f), ValueRef::UInt128(v) => fmt::Display::fmt(v, f), + ValueRef::UInt256(v) => fmt::Display::fmt(v, f), ValueRef::Int8(v) => fmt::Display::fmt(v, f), ValueRef::Int16(v) => fmt::Display::fmt(v, f), ValueRef::Int32(v) => fmt::Display::fmt(v, f), ValueRef::Int64(v) => fmt::Display::fmt(v, f), ValueRef::Int128(v) => fmt::Display::fmt(v, f), + ValueRef::Int256(v) => fmt::Display::fmt(v, f), ValueRef::String(v) => match str::from_utf8(v) { Ok(s) => fmt::Display::fmt(s, f), Err(_) => write!(f, "{:?}", *v), @@ -211,11 +220,13 @@ impl<'a> From> for SqlType { ValueRef::UInt32(_) => SqlType::UInt32, ValueRef::UInt64(_) => SqlType::UInt64, ValueRef::UInt128(_) => SqlType::UInt128, + ValueRef::UInt256(_) => SqlType::UInt256, ValueRef::Int8(_) => SqlType::Int8, ValueRef::Int16(_) => SqlType::Int16, ValueRef::Int32(_) => SqlType::Int32, ValueRef::Int64(_) => SqlType::Int64, ValueRef::Int128(_) => SqlType::Int128, + ValueRef::Int256(_) => SqlType::Int256, ValueRef::String(_) => SqlType::String, ValueRef::Float32(_) => SqlType::Float32, ValueRef::Float64(_) => SqlType::Float64, @@ -279,11 +290,13 @@ impl<'a> From> for Value { ValueRef::UInt32(v) => Value::UInt32(v), ValueRef::UInt64(v) => Value::UInt64(v), ValueRef::UInt128(v) => Value::UInt128(v), + ValueRef::UInt256(v) => Value::UInt256(v), ValueRef::Int8(v) => Value::Int8(v), ValueRef::Int16(v) => Value::Int16(v), ValueRef::Int32(v) => Value::Int32(v), ValueRef::Int64(v) => Value::Int64(v), ValueRef::Int128(v) => Value::Int128(v), + ValueRef::Int256(v) => Value::Int256(v), ValueRef::String(v) => Value::String(Arc::new(v.into())), ValueRef::Float32(v) => Value::Float32(v), ValueRef::Float64(v) => Value::Float64(v), @@ -356,12 +369,14 @@ from_number! { u32: UInt32, u64: UInt64, u128: UInt128, + u256: UInt256, i8: Int8, i16: Int16, i32: Int32, i64: Int64, i128: Int128, + i256: Int256, f32: Float32, f64: Float64 @@ -376,11 +391,13 @@ impl<'a> From<&'a Value> for ValueRef<'a> { Value::UInt32(v) => ValueRef::UInt32(*v), Value::UInt64(v) => ValueRef::UInt64(*v), Value::UInt128(v) => ValueRef::UInt128(*v), + Value::UInt256(v) => ValueRef::UInt256(*v), Value::Int8(v) => ValueRef::Int8(*v), Value::Int16(v) => ValueRef::Int16(*v), Value::Int32(v) => ValueRef::Int32(*v), Value::Int64(v) => ValueRef::Int64(*v), Value::Int128(v) => ValueRef::Int128(*v), + Value::Int256(v) => ValueRef::Int256(*v), Value::String(v) => ValueRef::String(v), Value::Float32(v) => ValueRef::Float32(*v), Value::Float64(v) => ValueRef::Float64(*v), @@ -495,12 +512,14 @@ value_from! { u32: UInt32, u64: UInt64, u128: UInt128, + u256: UInt256, i8: Int8, i16: Int16, i32: Int32, i64: Int64, i128: Int128, + i256: Int256, f32: Float32, f64: Float64 @@ -525,12 +544,20 @@ mod test { assert_eq!("42".to_string(), format!("{}", ValueRef::UInt32(42))); assert_eq!("42".to_string(), format!("{}", ValueRef::UInt64(42))); assert_eq!("42".to_string(), format!("{}", ValueRef::UInt128(42))); + assert_eq!( + "42".to_string(), + format!("{}", ValueRef::UInt256(u256::new(42))) + ); assert_eq!("42".to_string(), format!("{}", ValueRef::Int8(42))); assert_eq!("42".to_string(), format!("{}", ValueRef::Int16(42))); assert_eq!("42".to_string(), format!("{}", ValueRef::Int32(42))); assert_eq!("42".to_string(), format!("{}", ValueRef::Int64(42))); assert_eq!("42".to_string(), format!("{}", ValueRef::Int128(42))); + assert_eq!( + "42".to_string(), + format!("{}", ValueRef::Int256(i256::new(42))) + ); assert_eq!("42".to_string(), format!("{}", ValueRef::Float32(42.0))); assert_eq!("42".to_string(), format!("{}", ValueRef::Float64(42.0))); @@ -591,7 +618,7 @@ mod test { #[test] fn test_size_of() { use std::mem; - assert_eq!(32, mem::size_of::<[ValueRef<'_>; 1]>()); + assert_eq!(40, mem::size_of::<[ValueRef<'_>; 1]>()); } #[test] @@ -601,12 +628,20 @@ mod test { assert_eq!(Value::from(ValueRef::UInt32(42)), Value::UInt32(42)); assert_eq!(Value::from(ValueRef::UInt64(42)), Value::UInt64(42)); assert_eq!(Value::from(ValueRef::UInt128(42)), Value::UInt128(42)); + assert_eq!( + Value::from(ValueRef::UInt256(u256::new(42))), + Value::UInt256(u256::new(42)) + ); assert_eq!(Value::from(ValueRef::Int8(42)), Value::Int8(42)); assert_eq!(Value::from(ValueRef::Int16(42)), Value::Int16(42)); assert_eq!(Value::from(ValueRef::Int32(42)), Value::Int32(42)); assert_eq!(Value::from(ValueRef::Int64(42)), Value::Int64(42)); assert_eq!(Value::from(ValueRef::Int128(42)), Value::Int128(42)); + assert_eq!( + Value::from(ValueRef::Int256(i256::new(42))), + Value::Int256(i256::new(42)) + ); assert_eq!(Value::from(ValueRef::Float32(42.0)), Value::Float32(42.0)); assert_eq!(Value::from(ValueRef::Float64(42.0)), Value::Float64(42.0)); @@ -655,12 +690,20 @@ mod test { assert_eq!(SqlType::from(ValueRef::UInt32(42)), SqlType::UInt32); assert_eq!(SqlType::from(ValueRef::UInt64(42)), SqlType::UInt64); assert_eq!(SqlType::from(ValueRef::UInt128(42)), SqlType::UInt128); + assert_eq!( + SqlType::from(ValueRef::UInt256(u256::new(42))), + SqlType::UInt256 + ); assert_eq!(SqlType::from(ValueRef::Int8(42)), SqlType::Int8); assert_eq!(SqlType::from(ValueRef::Int16(42)), SqlType::Int16); assert_eq!(SqlType::from(ValueRef::Int32(42)), SqlType::Int32); assert_eq!(SqlType::from(ValueRef::Int64(42)), SqlType::Int64); assert_eq!(SqlType::from(ValueRef::Int128(42)), SqlType::Int128); + assert_eq!( + SqlType::from(ValueRef::Int256(i256::new(42))), + SqlType::Int256 + ); assert_eq!(SqlType::from(ValueRef::Float32(42.0)), SqlType::Float32); assert_eq!(SqlType::from(ValueRef::Float64(42.0)), SqlType::Float64); diff --git a/tests/clickhouse.rs b/tests/clickhouse.rs index c128837..89392c2 100644 --- a/tests/clickhouse.rs +++ b/tests/clickhouse.rs @@ -14,6 +14,7 @@ use clickhouse_rs::{ types::{Complex, Decimal, Enum16, Enum8, FromSql, SqlType, Value}, Block, Options, Pool, }; +use ethnum::{i256, u256}; use futures_util::{ future, stream::{StreamExt, TryStreamExt}, @@ -2404,5 +2405,183 @@ async fn test_insert_big_block() -> Result<(), Error> { .await?; assert_eq!(format!("{:?}", expected.as_ref()), format!("{:?}", &actual)); + + Ok(()) +} + +#[tokio::test] +async fn test_int_256() -> Result<(), Error> { + let ddl = " + CREATE TABLE clickhouse_test_int_256 ( + i Int256, + u UInt256, + oi Nullable(Int256) + ) Engine=Memory"; + + let query = "SELECT i, u, oi FROM clickhouse_test_int_256"; + + let block = Block::new() + .column( + "i", + vec![ + i256::from_str_prefixed("-340282366920938463463374607431768211455000000").unwrap(), + 2_000_000i64.into(), + 3_000_000_000i64.into(), + ], + ) + .column( + "u", + vec![ + u256::from_str_prefixed("340282366920938463463374607431768211455000000").unwrap(), + 2_000_000u64.into(), + 3_000_000_000u64.into(), + ], + ) + .column( + "oi", + vec![ + Some( + i256::from_str_prefixed("-340282366920938463463374607431768211455000000") + .unwrap(), + ), + None, + Some(3_000_000_000i64.into()), + ], + ); + + let pool = Pool::new(database_url()); + + let mut c = pool.get_handle().await?; + c.execute("DROP TABLE IF EXISTS clickhouse_test_int_256") + .await?; + c.execute(ddl).await?; + c.insert("clickhouse_test_int_256", block).await?; + let block = c.query(query).fetch_all().await?; + + let i: i256 = block.get(0, "i")?; + let u: u256 = block.get(0, "u")?; + let oi: Option = block.get(0, "oi")?; + + assert_eq!( + i, + i256::from_str_prefixed("-340282366920938463463374607431768211455000000").unwrap() + ); + assert_eq!( + u, + u256::from_str_prefixed("340282366920938463463374607431768211455000000").unwrap() + ); + assert_eq!( + oi, + Some(i256::from_str_prefixed("-340282366920938463463374607431768211455000000").unwrap()) + ); + + Ok(()) +} + +#[tokio::test] +async fn test_iter_int_256() -> Result<(), Error> { + let ddl = " + CREATE TABLE clickhouse_test_iter_int_256 ( + i Int256, + u UInt256, + oi Nullable(Int256), + ou Nullable(UInt256) + ) Engine=Memory"; + + let query = "SELECT i, u, oi, ou FROM clickhouse_test_iter_int_256"; + + let block = Block::new() + .column( + "i", + vec![ + i256::from_str_prefixed("-340282366920938463463374607431768211455000000").unwrap(), + 2_000_000i64.into(), + 3_000_000_000i64.into(), + ], + ) + .column( + "u", + vec![ + u256::from_str_prefixed("340282366920938463463374607431768211455000000").unwrap(), + 2_000_000u64.into(), + 3_000_000_000u64.into(), + ], + ) + .column( + "oi", + vec![ + Some( + i256::from_str_prefixed("-340282366920938463463374607431768211455000000") + .unwrap(), + ), + None, + Some(3_000_000_000i64.into()), + ], + ) + .column( + "ou", + vec![ + Some( + u256::from_str_prefixed("340282366920938463463374607431768211455000000") + .unwrap(), + ), + None, + Some(3_000_000_000u64.into()), + ], + ); + + let pool = Pool::new(database_url()); + + let mut c = pool.get_handle().await?; + c.execute("DROP TABLE IF EXISTS clickhouse_test_iter_int_256") + .await?; + c.execute(ddl).await?; + c.insert("clickhouse_test_iter_int_256", block).await?; + let block = c.query(query).fetch_all().await?; + + let is: Vec<_> = block.get_column("i")?.iter::()?.copied().collect(); + assert_eq!( + is, + vec![ + i256::from_str_prefixed("-340282366920938463463374607431768211455000000").unwrap(), + 2_000_000i64.into(), + 3_000_000_000i64.into() + ] + ); + + let us: Vec<_> = block.get_column("u")?.iter::()?.copied().collect(); + assert_eq!( + us, + vec![ + u256::from_str_prefixed("340282366920938463463374607431768211455000000").unwrap(), + 2_000_000u64.into(), + 3_000_000_000u64.into() + ] + ); + + let ois: Vec<_> = block.get_column("oi")?.iter::>()?.collect(); + assert_eq!( + ois, + vec![ + Some( + &i256::from_str_prefixed("-340282366920938463463374607431768211455000000").unwrap() + ), + None, + Some(&3_000_000_000i64.into()) + ] + ); + + let ous: Vec<_> = block.get_column("ou")?.iter::>()?.collect(); + assert_eq!( + ous, + vec![ + Some( + &u256::from_str_prefixed("340282366920938463463374607431768211455000000").unwrap() + ), + None, + Some(&3_000_000_000u64.into()) + ] + ); + Ok(()) }