use libduckdb_sys::{
DUCKDB_TYPE, DUCKDB_TYPE_DUCKDB_TYPE_ARRAY, DUCKDB_TYPE_DUCKDB_TYPE_BIGINT,
DUCKDB_TYPE_DUCKDB_TYPE_BIT, DUCKDB_TYPE_DUCKDB_TYPE_BLOB, DUCKDB_TYPE_DUCKDB_TYPE_BOOLEAN,
DUCKDB_TYPE_DUCKDB_TYPE_DATE, DUCKDB_TYPE_DUCKDB_TYPE_DECIMAL, DUCKDB_TYPE_DUCKDB_TYPE_DOUBLE,
DUCKDB_TYPE_DUCKDB_TYPE_ENUM, DUCKDB_TYPE_DUCKDB_TYPE_FLOAT, DUCKDB_TYPE_DUCKDB_TYPE_HUGEINT,
DUCKDB_TYPE_DUCKDB_TYPE_INTEGER, DUCKDB_TYPE_DUCKDB_TYPE_INTERVAL,
DUCKDB_TYPE_DUCKDB_TYPE_LIST, DUCKDB_TYPE_DUCKDB_TYPE_MAP, DUCKDB_TYPE_DUCKDB_TYPE_SMALLINT,
DUCKDB_TYPE_DUCKDB_TYPE_STRUCT, DUCKDB_TYPE_DUCKDB_TYPE_TIME,
DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP, DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_MS,
DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_NS, DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_S,
DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_TZ, DUCKDB_TYPE_DUCKDB_TYPE_TIME_TZ,
DUCKDB_TYPE_DUCKDB_TYPE_TINYINT, DUCKDB_TYPE_DUCKDB_TYPE_UBIGINT,
DUCKDB_TYPE_DUCKDB_TYPE_UHUGEINT, DUCKDB_TYPE_DUCKDB_TYPE_UINTEGER,
DUCKDB_TYPE_DUCKDB_TYPE_UNION, DUCKDB_TYPE_DUCKDB_TYPE_USMALLINT,
DUCKDB_TYPE_DUCKDB_TYPE_UTINYINT, DUCKDB_TYPE_DUCKDB_TYPE_UUID,
DUCKDB_TYPE_DUCKDB_TYPE_VARCHAR,
};
#[cfg(feature = "duckdb-1-5")]
use libduckdb_sys::{
DUCKDB_TYPE_DUCKDB_TYPE_ANY, DUCKDB_TYPE_DUCKDB_TYPE_BIGNUM,
DUCKDB_TYPE_DUCKDB_TYPE_INTEGER_LITERAL, DUCKDB_TYPE_DUCKDB_TYPE_SQLNULL,
DUCKDB_TYPE_DUCKDB_TYPE_STRING_LITERAL, DUCKDB_TYPE_DUCKDB_TYPE_TIME_NS,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum TypeId {
Boolean,
TinyInt,
SmallInt,
Integer,
BigInt,
UTinyInt,
USmallInt,
UInteger,
UBigInt,
HugeInt,
Float,
Double,
Timestamp,
TimestampTz,
Date,
Time,
Interval,
Varchar,
Blob,
Decimal,
TimestampS,
TimestampMs,
TimestampNs,
Enum,
List,
Struct,
Map,
Uuid,
Union,
Bit,
TimeTz,
UHugeInt,
Array,
#[cfg(feature = "duckdb-1-5")]
TimeNs,
#[cfg(feature = "duckdb-1-5")]
Any,
#[cfg(feature = "duckdb-1-5")]
Varint,
#[cfg(feature = "duckdb-1-5")]
SqlNull,
#[cfg(feature = "duckdb-1-5")]
IntegerLiteral,
#[cfg(feature = "duckdb-1-5")]
StringLiteral,
}
impl TypeId {
#[must_use]
pub const fn to_duckdb_type(self) -> DUCKDB_TYPE {
match self {
Self::Boolean => DUCKDB_TYPE_DUCKDB_TYPE_BOOLEAN,
Self::TinyInt => DUCKDB_TYPE_DUCKDB_TYPE_TINYINT,
Self::SmallInt => DUCKDB_TYPE_DUCKDB_TYPE_SMALLINT,
Self::Integer => DUCKDB_TYPE_DUCKDB_TYPE_INTEGER,
Self::BigInt => DUCKDB_TYPE_DUCKDB_TYPE_BIGINT,
Self::UTinyInt => DUCKDB_TYPE_DUCKDB_TYPE_UTINYINT,
Self::USmallInt => DUCKDB_TYPE_DUCKDB_TYPE_USMALLINT,
Self::UInteger => DUCKDB_TYPE_DUCKDB_TYPE_UINTEGER,
Self::UBigInt => DUCKDB_TYPE_DUCKDB_TYPE_UBIGINT,
Self::HugeInt => DUCKDB_TYPE_DUCKDB_TYPE_HUGEINT,
Self::Float => DUCKDB_TYPE_DUCKDB_TYPE_FLOAT,
Self::Double => DUCKDB_TYPE_DUCKDB_TYPE_DOUBLE,
Self::Timestamp => DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP,
Self::TimestampTz => DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_TZ,
Self::Date => DUCKDB_TYPE_DUCKDB_TYPE_DATE,
Self::Time => DUCKDB_TYPE_DUCKDB_TYPE_TIME,
Self::Interval => DUCKDB_TYPE_DUCKDB_TYPE_INTERVAL,
Self::Varchar => DUCKDB_TYPE_DUCKDB_TYPE_VARCHAR,
Self::Blob => DUCKDB_TYPE_DUCKDB_TYPE_BLOB,
Self::Decimal => DUCKDB_TYPE_DUCKDB_TYPE_DECIMAL,
Self::TimestampS => DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_S,
Self::TimestampMs => DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_MS,
Self::TimestampNs => DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_NS,
Self::Enum => DUCKDB_TYPE_DUCKDB_TYPE_ENUM,
Self::List => DUCKDB_TYPE_DUCKDB_TYPE_LIST,
Self::Struct => DUCKDB_TYPE_DUCKDB_TYPE_STRUCT,
Self::Map => DUCKDB_TYPE_DUCKDB_TYPE_MAP,
Self::Uuid => DUCKDB_TYPE_DUCKDB_TYPE_UUID,
Self::Union => DUCKDB_TYPE_DUCKDB_TYPE_UNION,
Self::Bit => DUCKDB_TYPE_DUCKDB_TYPE_BIT,
Self::TimeTz => DUCKDB_TYPE_DUCKDB_TYPE_TIME_TZ,
Self::UHugeInt => DUCKDB_TYPE_DUCKDB_TYPE_UHUGEINT,
Self::Array => DUCKDB_TYPE_DUCKDB_TYPE_ARRAY,
#[cfg(feature = "duckdb-1-5")]
Self::TimeNs => DUCKDB_TYPE_DUCKDB_TYPE_TIME_NS,
#[cfg(feature = "duckdb-1-5")]
Self::Any => DUCKDB_TYPE_DUCKDB_TYPE_ANY,
#[cfg(feature = "duckdb-1-5")]
Self::Varint => DUCKDB_TYPE_DUCKDB_TYPE_BIGNUM,
#[cfg(feature = "duckdb-1-5")]
Self::SqlNull => DUCKDB_TYPE_DUCKDB_TYPE_SQLNULL,
#[cfg(feature = "duckdb-1-5")]
Self::IntegerLiteral => DUCKDB_TYPE_DUCKDB_TYPE_INTEGER_LITERAL,
#[cfg(feature = "duckdb-1-5")]
Self::StringLiteral => DUCKDB_TYPE_DUCKDB_TYPE_STRING_LITERAL,
}
}
#[must_use]
pub const fn from_duckdb_type(raw: DUCKDB_TYPE) -> Self {
if raw == DUCKDB_TYPE_DUCKDB_TYPE_BOOLEAN {
Self::Boolean
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_TINYINT {
Self::TinyInt
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_SMALLINT {
Self::SmallInt
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_INTEGER {
Self::Integer
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_BIGINT {
Self::BigInt
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_UTINYINT {
Self::UTinyInt
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_USMALLINT {
Self::USmallInt
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_UINTEGER {
Self::UInteger
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_UBIGINT {
Self::UBigInt
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_HUGEINT {
Self::HugeInt
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_FLOAT {
Self::Float
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_DOUBLE {
Self::Double
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP {
Self::Timestamp
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_TZ {
Self::TimestampTz
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_DATE {
Self::Date
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_TIME {
Self::Time
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_INTERVAL {
Self::Interval
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_VARCHAR {
Self::Varchar
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_BLOB {
Self::Blob
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_DECIMAL {
Self::Decimal
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_S {
Self::TimestampS
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_MS {
Self::TimestampMs
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP_NS {
Self::TimestampNs
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_ENUM {
Self::Enum
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_LIST {
Self::List
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_STRUCT {
Self::Struct
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_MAP {
Self::Map
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_UUID {
Self::Uuid
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_UNION {
Self::Union
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_BIT {
Self::Bit
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_TIME_TZ {
Self::TimeTz
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_UHUGEINT {
Self::UHugeInt
} else if raw == DUCKDB_TYPE_DUCKDB_TYPE_ARRAY {
Self::Array
} else {
panic!("unknown DUCKDB_TYPE value")
}
}
#[must_use]
pub const fn sql_name(self) -> &'static str {
match self {
Self::Boolean => "BOOLEAN",
Self::TinyInt => "TINYINT",
Self::SmallInt => "SMALLINT",
Self::Integer => "INTEGER",
Self::BigInt => "BIGINT",
Self::UTinyInt => "UTINYINT",
Self::USmallInt => "USMALLINT",
Self::UInteger => "UINTEGER",
Self::UBigInt => "UBIGINT",
Self::HugeInt => "HUGEINT",
Self::Float => "FLOAT",
Self::Double => "DOUBLE",
Self::Timestamp => "TIMESTAMP",
Self::TimestampTz => "TIMESTAMPTZ",
Self::Date => "DATE",
Self::Time => "TIME",
Self::Interval => "INTERVAL",
Self::Varchar => "VARCHAR",
Self::Blob => "BLOB",
Self::Decimal => "DECIMAL",
Self::TimestampS => "TIMESTAMP_S",
Self::TimestampMs => "TIMESTAMP_MS",
Self::TimestampNs => "TIMESTAMP_NS",
Self::Enum => "ENUM",
Self::List => "LIST",
Self::Struct => "STRUCT",
Self::Map => "MAP",
Self::Uuid => "UUID",
Self::Union => "UNION",
Self::Bit => "BIT",
Self::TimeTz => "TIMETZ",
Self::UHugeInt => "UHUGEINT",
Self::Array => "ARRAY",
#[cfg(feature = "duckdb-1-5")]
Self::TimeNs => "TIME_NS",
#[cfg(feature = "duckdb-1-5")]
Self::Any => "ANY",
#[cfg(feature = "duckdb-1-5")]
Self::Varint => "VARINT",
#[cfg(feature = "duckdb-1-5")]
Self::SqlNull => "SQLNULL",
#[cfg(feature = "duckdb-1-5")]
Self::IntegerLiteral => "INTEGER_LITERAL",
#[cfg(feature = "duckdb-1-5")]
Self::StringLiteral => "STRING_LITERAL",
}
}
}
impl std::fmt::Display for TypeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.sql_name())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_types_round_trip_display() {
let types = [
TypeId::Boolean,
TypeId::TinyInt,
TypeId::SmallInt,
TypeId::Integer,
TypeId::BigInt,
TypeId::UTinyInt,
TypeId::USmallInt,
TypeId::UInteger,
TypeId::UBigInt,
TypeId::HugeInt,
TypeId::Float,
TypeId::Double,
TypeId::Timestamp,
TypeId::TimestampTz,
TypeId::Date,
TypeId::Time,
TypeId::Interval,
TypeId::Varchar,
TypeId::Blob,
TypeId::Decimal,
TypeId::TimestampS,
TypeId::TimestampMs,
TypeId::TimestampNs,
TypeId::Enum,
TypeId::List,
TypeId::Struct,
TypeId::Map,
TypeId::Uuid,
TypeId::Union,
TypeId::Bit,
TypeId::TimeTz,
TypeId::UHugeInt,
TypeId::Array,
#[cfg(feature = "duckdb-1-5")]
TypeId::TimeNs,
#[cfg(feature = "duckdb-1-5")]
TypeId::Any,
#[cfg(feature = "duckdb-1-5")]
TypeId::Varint,
#[cfg(feature = "duckdb-1-5")]
TypeId::SqlNull,
#[cfg(feature = "duckdb-1-5")]
TypeId::IntegerLiteral,
#[cfg(feature = "duckdb-1-5")]
TypeId::StringLiteral,
];
for t in types {
assert!(!t.sql_name().is_empty());
assert_eq!(t.sql_name(), format!("{t}"));
}
}
#[test]
fn bigint_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::BigInt.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_BIGINT
);
}
#[test]
fn boolean_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::Boolean.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_BOOLEAN
);
}
#[test]
fn timestamp_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::Timestamp.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_TIMESTAMP
);
}
#[test]
fn varchar_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::Varchar.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_VARCHAR
);
}
#[test]
fn type_id_clone_copy() {
let t = TypeId::Integer;
let t2 = t; assert_eq!(t, t2);
let t3 = t;
assert_eq!(t, t3);
}
#[test]
fn type_id_debug() {
let s = format!("{:?}", TypeId::Interval);
assert!(s.contains("Interval"));
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn time_ns_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::TimeNs.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_TIME_NS
);
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn any_maps_to_correct_duckdb_type() {
assert_eq!(TypeId::Any.to_duckdb_type(), DUCKDB_TYPE_DUCKDB_TYPE_ANY);
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn varint_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::Varint.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_BIGNUM
);
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn sql_null_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::SqlNull.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_SQLNULL
);
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn duckdb_1_5_variants_sql_names() {
assert_eq!(TypeId::TimeNs.sql_name(), "TIME_NS");
assert_eq!(TypeId::Any.sql_name(), "ANY");
assert_eq!(TypeId::Varint.sql_name(), "VARINT");
assert_eq!(TypeId::SqlNull.sql_name(), "SQLNULL");
assert_eq!(TypeId::IntegerLiteral.sql_name(), "INTEGER_LITERAL");
assert_eq!(TypeId::StringLiteral.sql_name(), "STRING_LITERAL");
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn duckdb_1_5_variants_display_matches_sql_name() {
assert_eq!(format!("{}", TypeId::TimeNs), "TIME_NS");
assert_eq!(format!("{}", TypeId::Any), "ANY");
assert_eq!(format!("{}", TypeId::Varint), "VARINT");
assert_eq!(format!("{}", TypeId::SqlNull), "SQLNULL");
assert_eq!(format!("{}", TypeId::IntegerLiteral), "INTEGER_LITERAL");
assert_eq!(format!("{}", TypeId::StringLiteral), "STRING_LITERAL");
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn duckdb_1_5_variants_hash_eq() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(TypeId::TimeNs);
set.insert(TypeId::Any);
set.insert(TypeId::Varint);
set.insert(TypeId::SqlNull);
set.insert(TypeId::IntegerLiteral);
set.insert(TypeId::StringLiteral);
assert_eq!(set.len(), 6);
assert!(set.contains(&TypeId::TimeNs));
assert!(set.contains(&TypeId::Any));
assert!(set.contains(&TypeId::Varint));
assert!(set.contains(&TypeId::SqlNull));
assert!(set.contains(&TypeId::IntegerLiteral));
assert!(set.contains(&TypeId::StringLiteral));
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn integer_literal_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::IntegerLiteral.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_INTEGER_LITERAL
);
}
#[cfg(feature = "duckdb-1-5")]
#[test]
fn string_literal_maps_to_correct_duckdb_type() {
assert_eq!(
TypeId::StringLiteral.to_duckdb_type(),
DUCKDB_TYPE_DUCKDB_TYPE_STRING_LITERAL
);
}
#[test]
fn from_duckdb_type_roundtrip() {
let variants = [
TypeId::Boolean,
TypeId::TinyInt,
TypeId::SmallInt,
TypeId::Integer,
TypeId::BigInt,
TypeId::UTinyInt,
TypeId::USmallInt,
TypeId::UInteger,
TypeId::UBigInt,
TypeId::HugeInt,
TypeId::Float,
TypeId::Double,
TypeId::Timestamp,
TypeId::TimestampTz,
TypeId::Date,
TypeId::Time,
TypeId::Interval,
TypeId::Varchar,
TypeId::Blob,
TypeId::Decimal,
TypeId::TimestampS,
TypeId::TimestampMs,
TypeId::TimestampNs,
TypeId::Enum,
TypeId::List,
TypeId::Struct,
TypeId::Map,
TypeId::Uuid,
TypeId::Union,
TypeId::Bit,
TypeId::TimeTz,
TypeId::UHugeInt,
TypeId::Array,
];
for &tid in &variants {
let raw = tid.to_duckdb_type();
let back = TypeId::from_duckdb_type(raw);
assert_eq!(back, tid, "roundtrip failed for {tid:?}");
}
}
}