From 11144dfc303ed7bee36c16657614b4b06db3a913 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 20 Mar 2024 15:29:21 +0800 Subject: [PATCH 01/13] kinda working --- src/row.rs | 1 + src/types/value.rs | 3 ++- src/types/value_ref.rs | 12 +++++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/row.rs b/src/row.rs index ac2c5104..242fcd01 100644 --- a/src/row.rs +++ b/src/row.rs @@ -620,6 +620,7 @@ impl<'stmt> Row<'stmt> { row, ) } + DataType::Struct(_) => ValueRef::Struct(column, row), _ => unreachable!("invalid value: {} {}", col, column.data_type()), } } diff --git a/src/types/value.rs b/src/types/value.rs index 78ee8b39..803d6f71 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -59,6 +59,7 @@ pub enum Value { List(Vec), /// The value is an enum Enum(String), + Struct(Vec), } impl From for Value { @@ -226,7 +227,7 @@ impl Value { Value::Date32(_) => Type::Date32, Value::Time64(..) => Type::Time64, Value::Interval { .. } => Type::Interval, - Value::List(_) => todo!(), + Value::Struct(_) | Value::List(_) => todo!(), Value::Enum(..) => Type::Enum, } } diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index d520f8f1..45e36942 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -4,7 +4,7 @@ use crate::types::{FromSqlError, FromSqlResult}; use crate::Row; use rust_decimal::prelude::*; -use arrow::array::{Array, DictionaryArray, ListArray}; +use arrow::array::{Array, ArrayRef, DictionaryArray, ListArray}; use arrow::datatypes::{UInt16Type, UInt32Type, UInt8Type}; /// An absolute length of time in seconds, milliseconds, microseconds or nanoseconds. @@ -78,6 +78,7 @@ pub enum ValueRef<'a> { List(&'a ListArray, usize), /// The value is an enum Enum(EnumType<'a>, usize), + Struct(&'a ArrayRef, usize), } /// Wrapper type for different enum sizes @@ -118,6 +119,7 @@ impl ValueRef<'_> { ValueRef::Interval { .. } => Type::Interval, ValueRef::List(arr, _) => arr.data_type().into(), ValueRef::Enum(..) => Type::Enum, + ValueRef::Struct(..) => todo!(), } } @@ -203,6 +205,13 @@ impl From> for Value { panic!("Enum value is not a string") } } + ValueRef::Struct(items, idx) => { + let range = 0..items.len(); + let map: Vec = range + .map(|row| Row::value_ref_internal(row, idx, items).to_owned()) + .collect(); + Value::Struct(map) + } } } } @@ -247,6 +256,7 @@ impl<'a> From<&'a Value> for ValueRef<'a> { Value::Interval { months, days, nanos } => ValueRef::Interval { months, days, nanos }, Value::List(..) => unimplemented!(), Value::Enum(..) => todo!(), + Value::Struct(..) => unimplemented!(), } } } From 2417a6825000b391183bf75390549a12cbcb621e Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 20 Mar 2024 18:52:57 +0800 Subject: [PATCH 02/13] add more docs --- src/types/value.rs | 1 + src/types/value_ref.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/types/value.rs b/src/types/value.rs index 803d6f71..78c5dfa9 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -59,6 +59,7 @@ pub enum Value { List(Vec), /// The value is an enum Enum(String), + /// The value is a struct Struct(Vec), } diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 45e36942..5a413059 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -78,6 +78,7 @@ pub enum ValueRef<'a> { List(&'a ListArray, usize), /// The value is an enum Enum(EnumType<'a>, usize), + /// The value is a struct Struct(&'a ArrayRef, usize), } From 435f526915e491abf07aeb64b614ad3563fff4d8 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 20 Mar 2024 19:27:30 +0800 Subject: [PATCH 03/13] iterate on struct support --- src/lib.rs | 2 +- src/row.rs | 5 ++++- src/types/value_ref.rs | 18 +++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 81e2a5d9..1f9975f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -604,6 +604,7 @@ mod test { Ok(()) } + #[test] #[cfg_attr(windows, ignore = "Windows doesn't allow concurrent writes to a file")] fn test_concurrent_transactions_busy_commit() -> Result<()> { @@ -1003,7 +1004,6 @@ mod test { } mod query_and_then_tests { - use super::*; #[derive(Debug)] diff --git a/src/row.rs b/src/row.rs index 242fcd01..37f5ebcb 100644 --- a/src/row.rs +++ b/src/row.rs @@ -620,7 +620,10 @@ impl<'stmt> Row<'stmt> { row, ) } - DataType::Struct(_) => ValueRef::Struct(column, row), + DataType::Struct(_) => { + let res = column.as_any().downcast_ref::().unwrap(); + ValueRef::Struct(res, row) + } _ => unreachable!("invalid value: {} {}", col, column.data_type()), } } diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 5a413059..80bce2b9 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -4,7 +4,7 @@ use crate::types::{FromSqlError, FromSqlResult}; use crate::Row; use rust_decimal::prelude::*; -use arrow::array::{Array, ArrayRef, DictionaryArray, ListArray}; +use arrow::array::{Array, ArrayRef, DictionaryArray, ListArray, StructArray}; use arrow::datatypes::{UInt16Type, UInt32Type, UInt8Type}; /// An absolute length of time in seconds, milliseconds, microseconds or nanoseconds. @@ -79,7 +79,7 @@ pub enum ValueRef<'a> { /// The value is an enum Enum(EnumType<'a>, usize), /// The value is a struct - Struct(&'a ArrayRef, usize), + Struct(&'a StructArray, usize), } /// Wrapper type for different enum sizes @@ -206,13 +206,13 @@ impl From> for Value { panic!("Enum value is not a string") } } - ValueRef::Struct(items, idx) => { - let range = 0..items.len(); - let map: Vec = range - .map(|row| Row::value_ref_internal(row, idx, items).to_owned()) - .collect(); - Value::Struct(map) - } + ValueRef::Struct(items, idx) => Value::Struct( + items + .columns() + .iter() + .map(|column| Row::value_ref_internal(0, idx, column).to_owned()) + .collect() + ) } } } From 8755efd95cb479aeea76c61f84f6ada2927a82e7 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 20 Mar 2024 19:32:45 +0800 Subject: [PATCH 04/13] start on map support --- src/lib.rs | 1 - src/row.rs | 4 ++++ src/types/value.rs | 4 +++- src/types/value_ref.rs | 31 ++++++++++++++++++++++++------- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1f9975f7..b08bb14c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -604,7 +604,6 @@ mod test { Ok(()) } - #[test] #[cfg_attr(windows, ignore = "Windows doesn't allow concurrent writes to a file")] fn test_concurrent_transactions_busy_commit() -> Result<()> { diff --git a/src/row.rs b/src/row.rs index 37f5ebcb..781e170f 100644 --- a/src/row.rs +++ b/src/row.rs @@ -624,6 +624,10 @@ impl<'stmt> Row<'stmt> { let res = column.as_any().downcast_ref::().unwrap(); ValueRef::Struct(res, row) } + DataType::Map(..) => { + let arr = column.as_any().downcast_ref::().unwrap(); + ValueRef::Map(arr, row) + } _ => unreachable!("invalid value: {} {}", col, column.data_type()), } } diff --git a/src/types/value.rs b/src/types/value.rs index 78c5dfa9..55062ebb 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -61,6 +61,8 @@ pub enum Value { Enum(String), /// The value is a struct Struct(Vec), + /// The value is a map + Map(Vec<(Value, Value)>), } impl From for Value { @@ -228,7 +230,7 @@ impl Value { Value::Date32(_) => Type::Date32, Value::Time64(..) => Type::Time64, Value::Interval { .. } => Type::Interval, - Value::Struct(_) | Value::List(_) => todo!(), + Value::Struct(_) | Value::List(_) | Value::Map(..) => todo!(), Value::Enum(..) => Type::Enum, } } diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 80bce2b9..344839e0 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -4,7 +4,7 @@ use crate::types::{FromSqlError, FromSqlResult}; use crate::Row; use rust_decimal::prelude::*; -use arrow::array::{Array, ArrayRef, DictionaryArray, ListArray, StructArray}; +use arrow::array::{Array, ArrayRef, DictionaryArray, ListArray, MapArray, StructArray}; use arrow::datatypes::{UInt16Type, UInt32Type, UInt8Type}; /// An absolute length of time in seconds, milliseconds, microseconds or nanoseconds. @@ -80,6 +80,8 @@ pub enum ValueRef<'a> { Enum(EnumType<'a>, usize), /// The value is a struct Struct(&'a StructArray, usize), + /// The value is a map + Map(&'a MapArray, usize), } /// Wrapper type for different enum sizes @@ -120,7 +122,7 @@ impl ValueRef<'_> { ValueRef::Interval { .. } => Type::Interval, ValueRef::List(arr, _) => arr.data_type().into(), ValueRef::Enum(..) => Type::Enum, - ValueRef::Struct(..) => todo!(), + ValueRef::Struct(..) | ValueRef::Map(..) => todo!(), } } @@ -210,9 +212,25 @@ impl From> for Value { items .columns() .iter() - .map(|column| Row::value_ref_internal(0, idx, column).to_owned()) - .collect() - ) + .map(|column| Row::value_ref_internal(idx, 0, column).to_owned()) + .collect(), + ), + ValueRef::Map(arr, idx) => { + let keys = arr.keys(); + let values = arr.values(); + let offsets = arr.offsets(); + let range = offsets[idx]..offsets[idx + 1]; + Value::Map( + range + .map(|row| { + let row = row.try_into().unwrap(); + let key = Row::value_ref_internal(row, idx, keys).to_owned(); + let value = Row::value_ref_internal(row, idx, values).to_owned(); + (key, value) + }) + .collect(), + ) + } } } } @@ -255,9 +273,8 @@ impl<'a> From<&'a Value> for ValueRef<'a> { Value::Date32(d) => ValueRef::Date32(d), Value::Time64(t, d) => ValueRef::Time64(t, d), Value::Interval { months, days, nanos } => ValueRef::Interval { months, days, nanos }, - Value::List(..) => unimplemented!(), Value::Enum(..) => todo!(), - Value::Struct(..) => unimplemented!(), + Value::List(..) | Value::Struct(..) | Value::Map(..) => unimplemented!(), } } } From 2e19607d19d02114f4efd2cc9170811f95f4a92c Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 20 Mar 2024 20:02:43 +0800 Subject: [PATCH 05/13] amend type enum --- src/types/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/types/mod.rs b/src/types/mod.rs index 79a7ad6a..8a843868 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -151,6 +151,10 @@ pub enum Type { List(Box), /// ENUM Enum, + /// STRUCT + Struct(Vec<(String, Type)>), + /// MAP + Map(Box, Box), /// Any Any, } @@ -220,8 +224,10 @@ impl fmt::Display for Type { Type::Date32 => f.pad("Date32"), Type::Time64 => f.pad("Time64"), Type::Interval => f.pad("Interval"), + Type::Struct(..) => f.pad("Struct"), Type::List(..) => f.pad("List"), Type::Enum => f.pad("Enum"), + Type::Map(..) => f.pad("Map"), Type::Any => f.pad("Any"), } } From f302b6586086456fca656b2a53f5a1370f4bd2f2 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Wed, 20 Mar 2024 21:08:17 +0800 Subject: [PATCH 06/13] add initial array support --- src/row.rs | 7 +++++-- src/types/mod.rs | 3 +++ src/types/value.rs | 2 ++ src/types/value_ref.rs | 17 +++++++++++++++-- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/row.rs b/src/row.rs index 781e170f..840e2003 100644 --- a/src/row.rs +++ b/src/row.rs @@ -3,7 +3,6 @@ use std::{convert, sync::Arc}; use super::{Error, Result, Statement}; use crate::types::{self, EnumType, FromSql, FromSqlError, ValueRef}; -use arrow::array::DictionaryArray; use arrow::{ array::{self, Array, ArrayRef, ListArray, StructArray}, datatypes::*, @@ -628,7 +627,11 @@ impl<'stmt> Row<'stmt> { let arr = column.as_any().downcast_ref::().unwrap(); ValueRef::Map(arr, row) } - _ => unreachable!("invalid value: {} {}", col, column.data_type()), + DataType::FixedSizeList(..) => { + let arr = column.as_any().downcast_ref::().unwrap(); + ValueRef::Array(arr, row) + } + _ => unreachable!("invalid value: {}, {}", col, column.data_type()), } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 8a843868..2c046301 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -155,6 +155,8 @@ pub enum Type { Struct(Vec<(String, Type)>), /// MAP Map(Box, Box), + /// ARRAY + Array(Box, u32), /// Any Any, } @@ -228,6 +230,7 @@ impl fmt::Display for Type { Type::List(..) => f.pad("List"), Type::Enum => f.pad("Enum"), Type::Map(..) => f.pad("Map"), + Type::Array(..) => f.pad("Array"), Type::Any => f.pad("Any"), } } diff --git a/src/types/value.rs b/src/types/value.rs index 55062ebb..8de65c2e 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -61,6 +61,8 @@ pub enum Value { Enum(String), /// The value is a struct Struct(Vec), + /// The value is an array + Array(Vec), /// The value is a map Map(Vec<(Value, Value)>), } diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 344839e0..7a870d60 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -4,7 +4,9 @@ use crate::types::{FromSqlError, FromSqlResult}; use crate::Row; use rust_decimal::prelude::*; -use arrow::array::{Array, ArrayRef, DictionaryArray, ListArray, MapArray, StructArray}; +use arrow::array::{ + Array, ArrayRef, DictionaryArray, FixedSizeListArray, ListArray, MapArray, StructArray, UnionArray, +}; use arrow::datatypes::{UInt16Type, UInt32Type, UInt8Type}; /// An absolute length of time in seconds, milliseconds, microseconds or nanoseconds. @@ -80,6 +82,8 @@ pub enum ValueRef<'a> { Enum(EnumType<'a>, usize), /// The value is a struct Struct(&'a StructArray, usize), + /// The value is an array + Array(&'a FixedSizeListArray, usize), /// The value is a map Map(&'a MapArray, usize), } @@ -231,6 +235,15 @@ impl From> for Value { .collect(), ) } + ValueRef::Array(items, idx) => { + let value_length = usize::try_from(items.value_length()).unwrap(); + let range = (idx * value_length)..((idx + 1) * value_length); + Value::Array( + range + .map(|row| Row::value_ref_internal(row, idx, items.values()).to_owned()) + .collect(), + ) + } } } } @@ -274,7 +287,7 @@ impl<'a> From<&'a Value> for ValueRef<'a> { Value::Time64(t, d) => ValueRef::Time64(t, d), Value::Interval { months, days, nanos } => ValueRef::Interval { months, days, nanos }, Value::Enum(..) => todo!(), - Value::List(..) | Value::Struct(..) | Value::Map(..) => unimplemented!(), + Value::List(..) | Value::Struct(..) | Value::Map(..) | Value::Array(..) => unimplemented!(), } } } From e084bd22845084f7c4d4e8fbd49fe42be74c02a4 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Thu, 21 Mar 2024 09:43:14 +0800 Subject: [PATCH 07/13] add conversion between DataType and Type --- src/types/mod.rs | 22 +++++++++++++++++++--- src/types/value_ref.rs | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/types/mod.rs b/src/types/mod.rs index 2c046301..e31e0cda 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -190,14 +190,30 @@ impl From<&DataType> for Type { DataType::Utf8 => Self::Text, // DataType::LargeUtf8 => Self::LargeUtf8, DataType::List(inner) => Self::List(Box::new(Type::from(inner.data_type()))), - // DataType::FixedSizeList(field, size) => Self::Array, + DataType::FixedSizeList(field, size) => { + Self::Array(Box::new(Type::from(field.data_type())), (*size).try_into().unwrap()) + } // DataType::LargeList(_) => Self::LargeList, - // DataType::Struct(inner) => Self::Struct, + DataType::Struct(inner) => Self::Struct( + inner + .iter() + .map(|f| (f.name().to_owned(), Type::from(f.data_type()))) + .collect(), + ), // DataType::Union(_, _) => Self::Union, // DataType::Dictionary(_, _) => Self::Enum, DataType::Decimal128(..) => Self::Decimal, DataType::Decimal256(..) => Self::Decimal, - // DataType::Map(field, ..) => Self::Map, + DataType::Map(field, ..) => { + let data_type = field.data_type(); + match data_type { + DataType::Struct(fields) => Self::Map( + Box::new(Type::from(fields[0].data_type())), + Box::new(Type::from(fields[1].data_type())), + ), + _ => unreachable!(), + } + } res => unimplemented!("{}", res), } } diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 7a870d60..8859a0d1 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -124,7 +124,10 @@ impl ValueRef<'_> { ValueRef::Date32(_) => Type::Date32, ValueRef::Time64(..) => Type::Time64, ValueRef::Interval { .. } => Type::Interval, + ValueRef::Struct(arr, _) => arr.data_type().into(), ValueRef::List(arr, _) => arr.data_type().into(), + ValueRef::Map(arr, _) => arr.data_type().into(), + ValueRef::Array(arr, _) => arr.data_type().into(), ValueRef::Enum(..) => Type::Enum, ValueRef::Struct(..) | ValueRef::Map(..) => todo!(), } From 04c71635f1e81730accbf782af8999740ef27805 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Fri, 22 Mar 2024 13:42:54 +0800 Subject: [PATCH 08/13] add full test_all_types() test --- src/test_all_types.rs | 262 +++++++++++++++++++++++++++++++++++++++--- src/types/value.rs | 2 +- 2 files changed, 250 insertions(+), 14 deletions(-) diff --git a/src/test_all_types.rs b/src/test_all_types.rs index 893088ac..5a1e8b4c 100644 --- a/src/test_all_types.rs +++ b/src/test_all_types.rs @@ -17,19 +17,6 @@ fn test_all_types() -> crate::Result<()> { "dec38_10", // union is currently blocked by https://github.com/duckdb/duckdb/pull/11326 "union", - // these remaining types are not yet supported by duckdb-rs - "struct", - "struct_of_arrays", - "array_of_structs", - "map", - "fixed_int_array", - "fixed_varchar_array", - "fixed_nested_int_array", - "fixed_nested_varchar_array", - "fixed_struct_array", - "struct_of_fixed_array", - "fixed_array_of_int_list", - "list_of_fixed_int_array", ]; let mut binding = database.prepare(&format!( @@ -239,6 +226,14 @@ fn test_single(idx: &mut i32, column: String, value: ValueRef) { } _ => assert_eq!(value, ValueRef::Null), }, + "float_array" => match idx { + 0 => assert_eq!(value.to_owned(), Value::List(vec![])), + 1 => assert_eq!( + value.to_owned(), + Value::List(vec![Value::Float(1.0), Value::Float(2.0)]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, "date_array" => match idx { 0 => assert_eq!(value.to_owned(), Value::List(vec![])), 1 => assert_eq!( @@ -322,6 +317,247 @@ fn test_single(idx: &mut i32, column: String, value: ValueRef) { } _ => assert_eq!(value, ValueRef::Null), }, + "struct" => match idx { + 0 => assert_eq!(value.to_owned(), Value::Struct(vec![Value::Null, Value::Null])), + 1 => assert_eq!( + value.to_owned(), + Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string()),]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "struct_of_arrays" => match idx { + 0 => assert_eq!(value.to_owned(), Value::Struct(vec![Value::Null, Value::Null,])), + 1 => assert_eq!( + value.to_owned(), + Value::Struct(vec![ + Value::List(vec![ + Value::Int(42), + Value::Int(999), + Value::Null, + Value::Null, + Value::Int(-42) + ]), + Value::List(vec![ + Value::Text("🦆🦆🦆🦆🦆🦆".to_string()), + Value::Text("goose".to_string()), + Value::Null, + Value::Text("".to_string()), + ]), + ],) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "array_of_structs" => match idx { + 0 => assert_eq!(value.to_owned(), Value::List(vec![])), + 1 => assert_eq!( + value.to_owned(), + Value::List(vec![ + Value::Struct(vec![Value::Null, Value::Null]), + Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())]), + Value::Null + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "map" => match idx { + 0 => assert_eq!(value.to_owned(), Value::Map(vec![])), + 1 => assert_eq!( + value.to_owned(), + Value::Map(vec![ + (Value::Text("key1".to_string()), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())), + (Value::Text("key2".to_string()), Value::Text("goose".to_string())), + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "fixed_int_array" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]) + ), + 1 => assert_eq!( + value.to_owned(), + Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "fixed_varchar_array" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::Text("a".to_string()), + Value::Null, + Value::Text("c".to_string()) + ]) + ), + 1 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::Text("d".to_string()), + Value::Text("e".to_string()), + Value::Text("f".to_string()) + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "fixed_nested_int_array" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]), + Value::Null, + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]) + ]) + ), + 1 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]), + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]), + Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]), + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "fixed_nested_varchar_array" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::Array(vec![ + Value::Text("a".to_string()), + Value::Null, + Value::Text("c".to_string()) + ]), + Value::Null, + Value::Array(vec![ + Value::Text("a".to_string()), + Value::Null, + Value::Text("c".to_string()) + ]) + ]) + ), + 1 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::Array(vec![ + Value::Text("d".to_string()), + Value::Text("e".to_string()), + Value::Text("f".to_string()) + ]), + Value::Array(vec![ + Value::Text("a".to_string()), + Value::Null, + Value::Text("c".to_string()) + ]), + Value::Array(vec![ + Value::Text("d".to_string()), + Value::Text("e".to_string()), + Value::Text("f".to_string()) + ]), + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "fixed_struct_array" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::Struct(vec![Value::Null, Value::Null]), + Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())]), + Value::Struct(vec![Value::Null, Value::Null]), + ]) + ), + 1 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())]), + Value::Struct(vec![Value::Null, Value::Null]), + Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())]), + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "struct_of_fixed_array" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::Struct(vec![ + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]), + Value::Array(vec![ + Value::Text("a".to_string()), + Value::Null, + Value::Text("c".to_string()) + ]), + ]) + ), + 1 => assert_eq!( + value.to_owned(), + Value::Struct(vec![ + Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]), + Value::Array(vec![ + Value::Text("d".to_string()), + Value::Text("e".to_string()), + Value::Text("f".to_string()) + ]), + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "fixed_array_of_int_list" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::List(vec![]), + Value::List(vec![ + Value::Int(42), + Value::Int(999), + Value::Null, + Value::Null, + Value::Int(-42), + ]), + Value::List(vec![]), + ]) + ), + 1 => assert_eq!( + value.to_owned(), + Value::Array(vec![ + Value::List(vec![ + Value::Int(42), + Value::Int(999), + Value::Null, + Value::Null, + Value::Int(-42), + ]), + Value::List(vec![]), + Value::List(vec![ + Value::Int(42), + Value::Int(999), + Value::Null, + Value::Null, + Value::Int(-42), + ]), + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, + "list_of_fixed_int_array" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::List(vec![ + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]), + Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]), + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]), + ]) + ), + 1 => assert_eq!( + value.to_owned(), + Value::List(vec![ + Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]), + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]), + Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]), + ]) + ), + _ => assert_eq!(value, ValueRef::Null), + }, "bit" => match idx { 0 => assert_eq!(value, ValueRef::Blob(&[1, 145, 46, 42, 215]),), 1 => assert_eq!(value, ValueRef::Blob(&[3, 245])), diff --git a/src/types/value.rs b/src/types/value.rs index 8de65c2e..79013379 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -232,7 +232,7 @@ impl Value { Value::Date32(_) => Type::Date32, Value::Time64(..) => Type::Time64, Value::Interval { .. } => Type::Interval, - Value::Struct(_) | Value::List(_) | Value::Map(..) => todo!(), + Value::Struct(..) | Value::List(..) | Value::Array(..) | Value::Map(..) => todo!(), Value::Enum(..) => Type::Enum, } } From 0635e21afdc907ab4b4359084aad00cc6206013a Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 24 Mar 2024 17:11:20 +0800 Subject: [PATCH 09/13] add basic union read support --- src/row.rs | 1 + src/test_all_types.rs | 8 ++++++++ src/types/mod.rs | 3 +++ src/types/value.rs | 4 +++- src/types/value_ref.rs | 16 ++++++++++++++-- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/row.rs b/src/row.rs index 840e2003..1d3a6ca5 100644 --- a/src/row.rs +++ b/src/row.rs @@ -631,6 +631,7 @@ impl<'stmt> Row<'stmt> { let arr = column.as_any().downcast_ref::().unwrap(); ValueRef::Array(arr, row) } + DataType::Union(..) => ValueRef::Union(column, row), _ => unreachable!("invalid value: {}, {}", col, column.data_type()), } } diff --git a/src/test_all_types.rs b/src/test_all_types.rs index 5a1e8b4c..5df0a088 100644 --- a/src/test_all_types.rs +++ b/src/test_all_types.rs @@ -597,6 +597,14 @@ fn test_single(idx: &mut i32, column: String, value: ValueRef) { 1 => assert_eq!(value.to_owned(), Value::Enum("enum_69999".to_string())), _ => assert_eq!(value, ValueRef::Null), }, + "union" => match idx { + 0 => assert_eq!( + value.to_owned(), + Value::Union(Box::new(Value::Text("Frank".to_owned()))) + ), + 1 => assert_eq!(value.to_owned(), Value::Union(Box::new(Value::SmallInt(5)))), + _ => assert_eq!(value.to_owned(), Value::Union(Box::new(Value::Null))), + }, _ => todo!("{column:?}"), } } diff --git a/src/types/mod.rs b/src/types/mod.rs index e31e0cda..8bd47a57 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -157,6 +157,8 @@ pub enum Type { Map(Box, Box), /// ARRAY Array(Box, u32), + /// UNION + Union, /// Any Any, } @@ -247,6 +249,7 @@ impl fmt::Display for Type { Type::Enum => f.pad("Enum"), Type::Map(..) => f.pad("Map"), Type::Array(..) => f.pad("Array"), + Type::Union => f.pad("Union"), Type::Any => f.pad("Any"), } } diff --git a/src/types/value.rs b/src/types/value.rs index 79013379..076ddb7d 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -65,6 +65,8 @@ pub enum Value { Array(Vec), /// The value is a map Map(Vec<(Value, Value)>), + /// The value is a union + Union(Box), } impl From for Value { @@ -232,7 +234,7 @@ impl Value { Value::Date32(_) => Type::Date32, Value::Time64(..) => Type::Time64, Value::Interval { .. } => Type::Interval, - Value::Struct(..) | Value::List(..) | Value::Array(..) | Value::Map(..) => todo!(), + Value::Union(..) | Value::Struct(..) | Value::List(..) | Value::Array(..) | Value::Map(..) => todo!(), Value::Enum(..) => Type::Enum, } } diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 8859a0d1..8905cad5 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -86,6 +86,8 @@ pub enum ValueRef<'a> { Array(&'a FixedSizeListArray, usize), /// The value is a map Map(&'a MapArray, usize), + /// The value is a union + Union(&'a ArrayRef, usize), } /// Wrapper type for different enum sizes @@ -129,7 +131,7 @@ impl ValueRef<'_> { ValueRef::Map(arr, _) => arr.data_type().into(), ValueRef::Array(arr, _) => arr.data_type().into(), ValueRef::Enum(..) => Type::Enum, - ValueRef::Struct(..) | ValueRef::Map(..) => todo!(), + ValueRef::Union(arr, _) => arr.data_type().into(), } } @@ -247,6 +249,14 @@ impl From> for Value { .collect(), ) } + ValueRef::Union(column, idx) => { + let column = column.as_any().downcast_ref::().unwrap(); + let type_id = column.type_id(idx); + let value_offset = column.value_offset(idx); + + let tag = Row::value_ref_internal(idx, value_offset, column.child(type_id)); + Value::Union(Box::new(tag.to_owned())) + } } } } @@ -290,7 +300,9 @@ impl<'a> From<&'a Value> for ValueRef<'a> { Value::Time64(t, d) => ValueRef::Time64(t, d), Value::Interval { months, days, nanos } => ValueRef::Interval { months, days, nanos }, Value::Enum(..) => todo!(), - Value::List(..) | Value::Struct(..) | Value::Map(..) | Value::Array(..) => unimplemented!(), + Value::List(..) | Value::Struct(..) | Value::Map(..) | Value::Array(..) | Value::Union(..) => { + unimplemented!() + } } } } From 7d272d039d9a8643b7552a5e2e3548dff970dec5 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sun, 31 Mar 2024 12:14:29 +0800 Subject: [PATCH 10/13] use better data structures --- src/test_all_types.rs | 151 +++++++++++++++++++++++++++------------ src/types/mod.rs | 3 + src/types/ordered_map.rs | 28 ++++++++ src/types/value.rs | 6 +- src/types/value_ref.rs | 22 +++--- 5 files changed, 151 insertions(+), 59 deletions(-) create mode 100644 src/types/ordered_map.rs diff --git a/src/test_all_types.rs b/src/test_all_types.rs index 5df0a088..0d6fbf75 100644 --- a/src/test_all_types.rs +++ b/src/test_all_types.rs @@ -2,7 +2,7 @@ use pretty_assertions::assert_eq; use rust_decimal::Decimal; use crate::{ - types::{TimeUnit, Type, Value, ValueRef}, + types::{OrderedMap, TimeUnit, Type, Value, ValueRef}, Connection, }; @@ -35,7 +35,7 @@ fn test_all_types() -> crate::Result<()> { for column in row.stmt.column_names() { let value = row.get_ref_unwrap(row.stmt.column_index(&column)?); if idx != 2 { - assert_ne!(value.data_type(), Type::Null); + assert_ne!(value.data_type(), Type::Null, "column {column} is null: {value:?}"); } test_single(&mut idx, column, value); } @@ -318,32 +318,53 @@ fn test_single(idx: &mut i32, column: String, value: ValueRef) { _ => assert_eq!(value, ValueRef::Null), }, "struct" => match idx { - 0 => assert_eq!(value.to_owned(), Value::Struct(vec![Value::Null, Value::Null])), + 0 => assert_eq!( + value.to_owned(), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Null), + ("b".to_string(), Value::Null), + ])) + ), 1 => assert_eq!( value.to_owned(), - Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string()),]) + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Int(42)), + ("b".to_string(), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())), + ])) ), _ => assert_eq!(value, ValueRef::Null), }, "struct_of_arrays" => match idx { - 0 => assert_eq!(value.to_owned(), Value::Struct(vec![Value::Null, Value::Null,])), + 0 => assert_eq!( + value.to_owned(), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Null), + ("b".to_string(), Value::Null), + ])) + ), 1 => assert_eq!( value.to_owned(), - Value::Struct(vec![ - Value::List(vec![ - Value::Int(42), - Value::Int(999), - Value::Null, - Value::Null, - Value::Int(-42) - ]), - Value::List(vec![ - Value::Text("🦆🦆🦆🦆🦆🦆".to_string()), - Value::Text("goose".to_string()), - Value::Null, - Value::Text("".to_string()), - ]), - ],) + Value::Struct(OrderedMap::from(vec![ + ( + "a".to_string(), + Value::List(vec![ + Value::Int(42), + Value::Int(999), + Value::Null, + Value::Null, + Value::Int(-42) + ]) + ), + ( + "b".to_string(), + Value::List(vec![ + Value::Text("🦆🦆🦆🦆🦆🦆".to_string()), + Value::Text("goose".to_string()), + Value::Null, + Value::Text("".to_string()), + ]), + ) + ])) ), _ => assert_eq!(value, ValueRef::Null), }, @@ -352,21 +373,27 @@ fn test_single(idx: &mut i32, column: String, value: ValueRef) { 1 => assert_eq!( value.to_owned(), Value::List(vec![ - Value::Struct(vec![Value::Null, Value::Null]), - Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())]), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Null), + ("b".to_string(), Value::Null) + ])), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Int(42)), + ("b".to_string(), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())) + ])), Value::Null ]) ), _ => assert_eq!(value, ValueRef::Null), }, "map" => match idx { - 0 => assert_eq!(value.to_owned(), Value::Map(vec![])), + 0 => assert_eq!(value.to_owned(), Value::Map(OrderedMap::from(vec![]))), 1 => assert_eq!( value.to_owned(), - Value::Map(vec![ + Value::Map(OrderedMap::from(vec![ (Value::Text("key1".to_string()), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())), (Value::Text("key2".to_string()), Value::Text("goose".to_string())), - ]) + ])) ), _ => assert_eq!(value, ValueRef::Null), }, @@ -462,17 +489,35 @@ fn test_single(idx: &mut i32, column: String, value: ValueRef) { 0 => assert_eq!( value.to_owned(), Value::Array(vec![ - Value::Struct(vec![Value::Null, Value::Null]), - Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())]), - Value::Struct(vec![Value::Null, Value::Null]), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Null), + ("b".to_string(), Value::Null) + ])), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Int(42)), + ("b".to_string(), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())) + ])), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Null), + ("b".to_string(), Value::Null) + ])), ]) ), 1 => assert_eq!( value.to_owned(), Value::Array(vec![ - Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())]), - Value::Struct(vec![Value::Null, Value::Null]), - Value::Struct(vec![Value::Int(42), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())]), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Int(42)), + ("b".to_string(), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())) + ])), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Null), + ("b".to_string(), Value::Null) + ])), + Value::Struct(OrderedMap::from(vec![ + ("a".to_string(), Value::Int(42)), + ("b".to_string(), Value::Text("🦆🦆🦆🦆🦆🦆".to_string())) + ])), ]) ), _ => assert_eq!(value, ValueRef::Null), @@ -480,25 +525,37 @@ fn test_single(idx: &mut i32, column: String, value: ValueRef) { "struct_of_fixed_array" => match idx { 0 => assert_eq!( value.to_owned(), - Value::Struct(vec![ - Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]), - Value::Array(vec![ - Value::Text("a".to_string()), - Value::Null, - Value::Text("c".to_string()) - ]), - ]) + Value::Struct(OrderedMap::from(vec![ + ( + "a".to_string(), + Value::Array(vec![Value::Null, Value::Int(2), Value::Int(3)]) + ), + ( + "b".to_string(), + Value::Array(vec![ + Value::Text("a".to_string()), + Value::Null, + Value::Text("c".to_string()) + ]) + ), + ])) ), 1 => assert_eq!( value.to_owned(), - Value::Struct(vec![ - Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]), - Value::Array(vec![ - Value::Text("d".to_string()), - Value::Text("e".to_string()), - Value::Text("f".to_string()) - ]), - ]) + Value::Struct(OrderedMap::from(vec![ + ( + "a".to_string(), + Value::Array(vec![Value::Int(4), Value::Int(5), Value::Int(6)]), + ), + ( + "b".to_string(), + Value::Array(vec![ + Value::Text("d".to_string()), + Value::Text("e".to_string()), + Value::Text("f".to_string()) + ]), + ) + ])) ), _ => assert_eq!(value, ValueRef::Null), }, diff --git a/src/types/mod.rs b/src/types/mod.rs index 8bd47a57..6efc6b8f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -69,6 +69,7 @@ impl ToSql for DateTimeSql { pub use self::{ from_sql::{FromSql, FromSqlError, FromSqlResult}, + ordered_map::OrderedMap, to_sql::{ToSql, ToSqlOutput}, value::Value, value_ref::{EnumType, TimeUnit, ValueRef}, @@ -88,6 +89,8 @@ mod url; mod value; mod value_ref; +mod ordered_map; + /// Empty struct that can be used to fill in a query parameter as `NULL`. /// /// ## Example diff --git a/src/types/ordered_map.rs b/src/types/ordered_map.rs new file mode 100644 index 00000000..09d9392f --- /dev/null +++ b/src/types/ordered_map.rs @@ -0,0 +1,28 @@ +/// An ordered map of key-value pairs. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct OrderedMap(Vec<(K, V)>); + +impl From> for OrderedMap { + fn from(value: Vec<(K, V)>) -> Self { + OrderedMap(value) + } +} + +impl OrderedMap { + /// Returns the value corresponding to the key. + pub fn get(&self, key: &K) -> Option<&V> { + self.0.iter().find(|(k, _)| k == key).map(|(_, v)| v) + } + /// Returns an iterator over the keys in the map. + pub fn keys(&self) -> impl Iterator { + self.0.iter().map(|(k, _)| k) + } + /// Returns an iterator over the values in the map. + pub fn values(&self) -> impl Iterator { + self.0.iter().map(|(_, v)| v) + } + /// Returns an iterator over the key-value pairs in the map. + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } +} diff --git a/src/types/value.rs b/src/types/value.rs index 076ddb7d..03c7b3dd 100644 --- a/src/types/value.rs +++ b/src/types/value.rs @@ -1,4 +1,4 @@ -use super::{Null, TimeUnit, Type}; +use super::{Null, OrderedMap, TimeUnit, Type}; use rust_decimal::prelude::*; /// Owning [dynamic type value](http://sqlite.org/datatype3.html). Value's type is typically @@ -60,11 +60,11 @@ pub enum Value { /// The value is an enum Enum(String), /// The value is a struct - Struct(Vec), + Struct(OrderedMap), /// The value is an array Array(Vec), /// The value is a map - Map(Vec<(Value, Value)>), + Map(OrderedMap), /// The value is a union Union(Box), } diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs index 8905cad5..d607d402 100644 --- a/src/types/value_ref.rs +++ b/src/types/value_ref.rs @@ -1,5 +1,5 @@ use super::{Type, Value}; -use crate::types::{FromSqlError, FromSqlResult}; +use crate::types::{FromSqlError, FromSqlResult, OrderedMap}; use crate::Row; use rust_decimal::prelude::*; @@ -217,19 +217,23 @@ impl From> for Value { panic!("Enum value is not a string") } } - ValueRef::Struct(items, idx) => Value::Struct( - items + ValueRef::Struct(items, idx) => { + let value: Vec<(String, Value)> = items .columns() .iter() - .map(|column| Row::value_ref_internal(idx, 0, column).to_owned()) - .collect(), - ), + .zip(items.fields().iter().map(|f| f.name().to_owned())) + .map(|(column, name)| -> (String, Value) { + (name, Row::value_ref_internal(idx, 0, column).to_owned()) + }) + .collect(); + Value::Struct(OrderedMap::from(value)) + } ValueRef::Map(arr, idx) => { let keys = arr.keys(); let values = arr.values(); let offsets = arr.offsets(); let range = offsets[idx]..offsets[idx + 1]; - Value::Map( + Value::Map(OrderedMap::from( range .map(|row| { let row = row.try_into().unwrap(); @@ -237,8 +241,8 @@ impl From> for Value { let value = Row::value_ref_internal(row, idx, values).to_owned(); (key, value) }) - .collect(), - ) + .collect::>(), + )) } ValueRef::Array(items, idx) => { let value_length = usize::try_from(items.value_length()).unwrap(); From e9f77e96a8f4cff783785e73772a51ad68d666f0 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Thu, 18 Apr 2024 12:06:47 +0800 Subject: [PATCH 11/13] chore: fix up imports --- src/row.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/row.rs b/src/row.rs index 1d3a6ca5..773f7b21 100644 --- a/src/row.rs +++ b/src/row.rs @@ -4,7 +4,7 @@ use super::{Error, Result, Statement}; use crate::types::{self, EnumType, FromSql, FromSqlError, ValueRef}; use arrow::{ - array::{self, Array, ArrayRef, ListArray, StructArray}, + array::{self, Array, ArrayRef, DictionaryArray, FixedSizeListArray, ListArray, MapArray, StructArray}, datatypes::*, }; use fallible_iterator::FallibleIterator; From b5f2de32629b89a8e3ac539f065bb750d7df20ac Mon Sep 17 00:00:00 2001 From: Elliana May Date: Mon, 22 Apr 2024 00:10:13 +0800 Subject: [PATCH 12/13] Update test_all_types.rs --- src/test_all_types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test_all_types.rs b/src/test_all_types.rs index 0d6fbf75..6c4c6a34 100644 --- a/src/test_all_types.rs +++ b/src/test_all_types.rs @@ -10,7 +10,7 @@ use crate::{ fn test_all_types() -> crate::Result<()> { let database = Connection::open_in_memory()?; - let excluded = vec![ + let excluded = [ // uhugeint, time_tz, and dec38_10 aren't supported in the duckdb arrow layer "uhugeint", "time_tz", From 017a42d057d3adb097d724a4eee450c6879a5294 Mon Sep 17 00:00:00 2001 From: Elliana May Date: Sat, 8 Jun 2024 14:44:03 +0800 Subject: [PATCH 13/13] clippy --- crates/duckdb/src/test_all_types.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/duckdb/src/test_all_types.rs b/crates/duckdb/src/test_all_types.rs index 40c36d55..4088be1b 100644 --- a/crates/duckdb/src/test_all_types.rs +++ b/crates/duckdb/src/test_all_types.rs @@ -20,12 +20,9 @@ fn test_large_arrow_types() -> crate::Result<()> { } fn test_with_database(database: &Connection) -> crate::Result<()> { - let excluded = vec![ - // uhugeint, time_tz, and dec38_10 aren't supported in the duckdb arrow layer - "uhugeint", "time_tz", "dec38_10", - // union is currently blocked by https://github.com/duckdb/duckdb/pull/11326 - "union", - ]; + // uhugeint, time_tz, and dec38_10 aren't supported in the duckdb arrow layer + // union is currently blocked by https://github.com/duckdb/duckdb/pull/11326 + let excluded = ["uhugeint", "time_tz", "dec38_10", "union"]; let mut binding = database.prepare(&format!( "SELECT * EXCLUDE ({}) FROM test_all_types()",