@@ -9,7 +9,10 @@ use crate::ffi::*;
99/// <https://duckdb.org/docs/api/c/types>
1010#[ repr( u32 ) ]
1111#[ derive( Debug , PartialEq , Eq ) ]
12+ #[ non_exhaustive]
1213pub enum LogicalTypeId {
14+ /// Invalid
15+ Invalid = DUCKDB_TYPE_DUCKDB_TYPE_INVALID ,
1316 /// Boolean
1417 Boolean = DUCKDB_TYPE_DUCKDB_TYPE_BOOLEAN ,
1518 /// Tinyint
@@ -66,14 +69,37 @@ pub enum LogicalTypeId {
6669 Uuid = DUCKDB_TYPE_DUCKDB_TYPE_UUID ,
6770 /// Union
6871 Union = DUCKDB_TYPE_DUCKDB_TYPE_UNION ,
72+ /// Bit
73+ Bit = DUCKDB_TYPE_DUCKDB_TYPE_BIT ,
74+ /// Time TZ
75+ TimeTZ = DUCKDB_TYPE_DUCKDB_TYPE_TIME_TZ ,
6976 /// Timestamp TZ
7077 TimestampTZ = DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_TZ ,
78+ /// Unsigned Hugeint
79+ UHugeint = DUCKDB_TYPE_DUCKDB_TYPE_UHUGEINT ,
80+ /// Array
81+ Array = DUCKDB_TYPE_DUCKDB_TYPE_ARRAY ,
82+ /// Any
83+ Any = DUCKDB_TYPE_DUCKDB_TYPE_ANY ,
84+ /// Bignum
85+ Bignum = DUCKDB_TYPE_DUCKDB_TYPE_BIGNUM ,
86+ /// SqlNull
87+ SqlNull = DUCKDB_TYPE_DUCKDB_TYPE_SQLNULL ,
88+ /// String Literal
89+ StringLiteral = DUCKDB_TYPE_DUCKDB_TYPE_STRING_LITERAL ,
90+ /// Integer Literal
91+ IntegerLiteral = DUCKDB_TYPE_DUCKDB_TYPE_INTEGER_LITERAL ,
92+ /// Time NS
93+ TimeNs = DUCKDB_TYPE_DUCKDB_TYPE_TIME_NS ,
94+ /// DuckDB returned a type that this wrapper does not yet recognize
95+ Unsupported = u32:: MAX ,
7196}
7297
7398impl From < u32 > for LogicalTypeId {
7499 /// Convert from u32 to LogicalTypeId
75100 fn from ( value : u32 ) -> Self {
76101 match value {
102+ DUCKDB_TYPE_DUCKDB_TYPE_INVALID => Self :: Invalid ,
77103 DUCKDB_TYPE_DUCKDB_TYPE_BOOLEAN => Self :: Boolean ,
78104 DUCKDB_TYPE_DUCKDB_TYPE_TINYINT => Self :: Tinyint ,
79105 DUCKDB_TYPE_DUCKDB_TYPE_SMALLINT => Self :: Smallint ,
@@ -102,8 +128,19 @@ impl From<u32> for LogicalTypeId {
102128 DUCKDB_TYPE_DUCKDB_TYPE_MAP => Self :: Map ,
103129 DUCKDB_TYPE_DUCKDB_TYPE_UUID => Self :: Uuid ,
104130 DUCKDB_TYPE_DUCKDB_TYPE_UNION => Self :: Union ,
131+ DUCKDB_TYPE_DUCKDB_TYPE_BIT => Self :: Bit ,
132+ DUCKDB_TYPE_DUCKDB_TYPE_TIME_TZ => Self :: TimeTZ ,
105133 DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_TZ => Self :: TimestampTZ ,
106- _ => panic ! ( ) ,
134+ DUCKDB_TYPE_DUCKDB_TYPE_UHUGEINT => Self :: UHugeint ,
135+ DUCKDB_TYPE_DUCKDB_TYPE_ARRAY => Self :: Array ,
136+ DUCKDB_TYPE_DUCKDB_TYPE_ANY => Self :: Any ,
137+ DUCKDB_TYPE_DUCKDB_TYPE_BIGNUM => Self :: Bignum ,
138+ DUCKDB_TYPE_DUCKDB_TYPE_SQLNULL => Self :: SqlNull ,
139+ DUCKDB_TYPE_DUCKDB_TYPE_STRING_LITERAL => Self :: StringLiteral ,
140+ DUCKDB_TYPE_DUCKDB_TYPE_INTEGER_LITERAL => Self :: IntegerLiteral ,
141+ DUCKDB_TYPE_DUCKDB_TYPE_TIME_NS => Self :: TimeNs ,
142+ // Unknown / forward compatible types
143+ _ => Self :: Unsupported ,
107144 }
108145 }
109146}
@@ -119,6 +156,8 @@ impl Debug for LogicalTypeHandle {
119156 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
120157 let id = self . id ( ) ;
121158 match id {
159+ LogicalTypeId :: Invalid => write ! ( f, "Invalid" ) ,
160+ LogicalTypeId :: Unsupported => write ! ( f, "Unsupported({})" , self . raw_id( ) ) ,
122161 LogicalTypeId :: Struct => {
123162 write ! ( f, "struct<" ) ?;
124163 for i in 0 ..self . num_children ( ) {
@@ -129,7 +168,7 @@ impl Debug for LogicalTypeHandle {
129168 }
130169 write ! ( f, ">" )
131170 }
132- _ => write ! ( f, "{:?}" , self . id ( ) ) ,
171+ _ => write ! ( f, "{:?}" , id ) ,
133172 }
134173 }
135174}
@@ -248,8 +287,25 @@ impl LogicalTypeHandle {
248287
249288 /// Logical type ID
250289 pub fn id ( & self ) -> LogicalTypeId {
251- let duckdb_type_id = unsafe { duckdb_get_type_id ( self . ptr ) } ;
252- duckdb_type_id. into ( )
290+ self . raw_id ( ) . into ( )
291+ }
292+
293+ /// Logical type ID, with forward-compatibility awareness.
294+ ///
295+ /// Returns `Ok(LogicalTypeId)` for all known ids (including `Invalid`), and
296+ /// `Err(raw_id)` when DuckDB returns an id this wrapper does not yet
297+ /// recognize.
298+ pub fn try_id ( & self ) -> Result < LogicalTypeId , u32 > {
299+ let raw = self . raw_id ( ) ;
300+ match LogicalTypeId :: from ( raw) {
301+ LogicalTypeId :: Unsupported => Err ( raw) ,
302+ id => Ok ( id) ,
303+ }
304+ }
305+
306+ /// Raw logical type id returned by DuckDB C API
307+ pub fn raw_id ( & self ) -> u32 {
308+ unsafe { duckdb_get_type_id ( self . ptr ) }
253309 }
254310
255311 /// Logical type children num
@@ -258,6 +314,7 @@ impl LogicalTypeHandle {
258314 LogicalTypeId :: Struct => unsafe { duckdb_struct_type_child_count ( self . ptr ) as usize } ,
259315 LogicalTypeId :: Union => unsafe { duckdb_union_type_member_count ( self . ptr ) as usize } ,
260316 LogicalTypeId :: List => 1 ,
317+ LogicalTypeId :: Array => 1 ,
261318 _ => 0 ,
262319 }
263320 }
@@ -270,6 +327,7 @@ impl LogicalTypeHandle {
270327 let child_name_ptr = match self . id ( ) {
271328 LogicalTypeId :: Struct => duckdb_struct_type_child_name ( self . ptr , idx as u64 ) ,
272329 LogicalTypeId :: Union => duckdb_union_type_member_name ( self . ptr , idx as u64 ) ,
330+ LogicalTypeId :: Unsupported => panic ! ( "unsupported logical type {}" , self . raw_id( ) ) ,
273331 _ => panic ! ( "not a struct or union" ) ,
274332 } ;
275333 let c_str = CString :: from_raw ( child_name_ptr) ;
@@ -284,7 +342,9 @@ impl LogicalTypeHandle {
284342 match self . id ( ) {
285343 LogicalTypeId :: Struct => duckdb_struct_type_child_type ( self . ptr , idx as u64 ) ,
286344 LogicalTypeId :: Union => duckdb_union_type_member_type ( self . ptr , idx as u64 ) ,
287- _ => panic ! ( "not a struct or union" ) ,
345+ LogicalTypeId :: Array => duckdb_array_type_child_type ( self . ptr ) ,
346+ LogicalTypeId :: Unsupported => panic ! ( "unsupported logical type {}" , self . raw_id( ) ) ,
347+ _ => panic ! ( "not a struct, union, or array" ) ,
288348 }
289349 } ;
290350 unsafe { Self :: new ( c_logical_type) }
@@ -319,26 +379,36 @@ mod test {
319379
320380 #[ test]
321381 fn test_struct ( ) {
322- let fields = & [ ( "hello" , LogicalTypeHandle :: from ( crate :: core :: LogicalTypeId :: Boolean ) ) ] ;
382+ let fields = & [ ( "hello" , LogicalTypeHandle :: from ( LogicalTypeId :: Boolean ) ) ] ;
323383 let typ = LogicalTypeHandle :: struct_type ( fields) ;
324384
325385 assert_eq ! ( typ. num_children( ) , 1 ) ;
326386 assert_eq ! ( typ. child_name( 0 ) , "hello" ) ;
327- assert_eq ! ( typ. child( 0 ) . id( ) , crate :: core:: LogicalTypeId :: Boolean ) ;
387+ assert_eq ! ( typ. child( 0 ) . id( ) , LogicalTypeId :: Boolean ) ;
388+ }
389+
390+ #[ test]
391+ fn test_array ( ) {
392+ let child = LogicalTypeHandle :: from ( LogicalTypeId :: Integer ) ;
393+ let array = LogicalTypeHandle :: array ( & child, 4 ) ;
394+
395+ assert_eq ! ( array. id( ) , LogicalTypeId :: Array ) ;
396+ assert_eq ! ( array. num_children( ) , 1 ) ;
397+ assert_eq ! ( array. child( 0 ) . id( ) , LogicalTypeId :: Integer ) ;
328398 }
329399
330400 #[ test]
331401 fn test_decimal ( ) {
332402 let typ = LogicalTypeHandle :: decimal ( 10 , 2 ) ;
333403
334- assert_eq ! ( typ. id( ) , crate :: core :: LogicalTypeId :: Decimal ) ;
404+ assert_eq ! ( typ. id( ) , LogicalTypeId :: Decimal ) ;
335405 assert_eq ! ( typ. decimal_width( ) , 10 ) ;
336406 assert_eq ! ( typ. decimal_scale( ) , 2 ) ;
337407 }
338408
339409 #[ test]
340410 fn test_decimal_methods ( ) {
341- let typ = LogicalTypeHandle :: from ( crate :: core :: LogicalTypeId :: Varchar ) ;
411+ let typ = LogicalTypeHandle :: from ( LogicalTypeId :: Varchar ) ;
342412
343413 assert_eq ! ( typ. decimal_width( ) , 0 ) ;
344414 assert_eq ! ( typ. decimal_scale( ) , 0 ) ;
@@ -360,4 +430,25 @@ mod test {
360430 assert_eq ! ( typ. child_name( 1 ) , "world" ) ;
361431 assert_eq ! ( typ. child( 1 ) . id( ) , LogicalTypeId :: Integer ) ;
362432 }
433+
434+ #[ test]
435+ fn test_invalid_type ( ) {
436+ use crate :: ffi:: { duckdb_create_logical_type, DUCKDB_TYPE_DUCKDB_TYPE_INVALID } ;
437+
438+ // Create an invalid logical type (what DuckDB returns in certain error cases)
439+ let invalid_type =
440+ unsafe { LogicalTypeHandle :: new ( duckdb_create_logical_type ( DUCKDB_TYPE_DUCKDB_TYPE_INVALID ) ) } ;
441+
442+ assert_eq ! ( invalid_type. id( ) , LogicalTypeId :: Invalid ) ;
443+ assert_eq ! ( invalid_type. try_id( ) . unwrap( ) , LogicalTypeId :: Invalid ) ;
444+ assert_eq ! ( invalid_type. raw_id( ) , DUCKDB_TYPE_DUCKDB_TYPE_INVALID ) ;
445+
446+ let debug_str = format ! ( "{invalid_type:?}" ) ;
447+ assert_eq ! ( debug_str, "Invalid" ) ;
448+ }
449+
450+ #[ test]
451+ fn test_unknown_type ( ) {
452+ assert_eq ! ( LogicalTypeId :: from( 999_999 ) , LogicalTypeId :: Unsupported ) ;
453+ }
363454}
0 commit comments