From af5befd581b68e420611ef2453c402e7c21467d4 Mon Sep 17 00:00:00 2001 From: rijkvp Date: Wed, 15 May 2024 11:52:27 +0200 Subject: [PATCH 1/3] Implement appender for date/time types --- crates/duckdb/src/appender/mod.rs | 22 +++++++++++++++------- crates/duckdb/src/types/value_ref.rs | 12 ++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/crates/duckdb/src/appender/mod.rs b/crates/duckdb/src/appender/mod.rs index 65b6db0f..cd4529be 100644 --- a/crates/duckdb/src/appender/mod.rs +++ b/crates/duckdb/src/appender/mod.rs @@ -118,15 +118,23 @@ impl Appender<'_> { ffi::duckdb_append_varchar_length(ptr, s.as_ptr() as *const c_char, s.len() as u64) }, ValueRef::Timestamp(u, i) => unsafe { - let micros = match u { - TimeUnit::Second => i * 1_000_000, - TimeUnit::Millisecond => i * 1_000, - TimeUnit::Microsecond => i, - TimeUnit::Nanosecond => i / 1_000, - }; - ffi::duckdb_append_timestamp(ptr, ffi::duckdb_timestamp { micros }) + ffi::duckdb_append_timestamp(ptr, ffi::duckdb_timestamp { micros: u.to_micros(i) }) }, ValueRef::Blob(b) => unsafe { ffi::duckdb_append_blob(ptr, b.as_ptr() as *const c_void, b.len() as u64) }, + ValueRef::Date32(d) => unsafe { ffi::duckdb_append_date(ptr, ffi::duckdb_date { days: d }) }, + ValueRef::Time64(u, v) => unsafe { + ffi::duckdb_append_time(ptr, ffi::duckdb_time { micros: u.to_micros(v) }) + }, + ValueRef::Interval { months, days, nanos } => unsafe { + ffi::duckdb_append_interval( + ptr, + ffi::duckdb_interval { + months, + days, + micros: nanos / 1000, + }, + ) + }, _ => unreachable!("not supported"), }; if rc != 0 { diff --git a/crates/duckdb/src/types/value_ref.rs b/crates/duckdb/src/types/value_ref.rs index db06c462..ed89ac01 100644 --- a/crates/duckdb/src/types/value_ref.rs +++ b/crates/duckdb/src/types/value_ref.rs @@ -23,6 +23,18 @@ pub enum TimeUnit { Nanosecond, } +impl TimeUnit { + /// Convert a number of `TimeUnit` to microseconds. + pub fn to_micros(&self, value: i64) -> i64 { + match self { + TimeUnit::Second => value * 1_000_000, + TimeUnit::Millisecond => value * 1000, + TimeUnit::Microsecond => value, + TimeUnit::Nanosecond => value / 1000, + } + } +} + /// A non-owning [static type value](https://duckdb.org/docs/sql/data_types/overview). Typically the /// memory backing this value is owned by SQLite. /// From 896fc85664dc2e936707a66118b774075313099e Mon Sep 17 00:00:00 2001 From: Rijk van Putten Date: Thu, 6 Jun 2024 08:12:56 +0200 Subject: [PATCH 2/3] Remove unused import --- crates/duckdb/src/appender/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/duckdb/src/appender/mod.rs b/crates/duckdb/src/appender/mod.rs index cd4529be..c43a19c1 100644 --- a/crates/duckdb/src/appender/mod.rs +++ b/crates/duckdb/src/appender/mod.rs @@ -3,7 +3,7 @@ use std::{ffi::c_void, fmt, os::raw::c_char}; use crate::{ error::result_from_duckdb_appender, - types::{TimeUnit, ToSql, ToSqlOutput}, + types::{ToSql, ToSqlOutput}, Error, }; From b6d32b34f32dbf9c09dc87fab4c9a2c0236def89 Mon Sep 17 00:00:00 2001 From: Rijk van Putten Date: Thu, 6 Jun 2024 08:42:04 +0200 Subject: [PATCH 3/3] Add unit test for date/time append --- crates/duckdb/src/appender/mod.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/crates/duckdb/src/appender/mod.rs b/crates/duckdb/src/appender/mod.rs index c43a19c1..488db82f 100644 --- a/crates/duckdb/src/appender/mod.rs +++ b/crates/duckdb/src/appender/mod.rs @@ -263,6 +263,29 @@ mod test { Ok(()) } + #[test] + #[cfg(feature = "chrono")] + fn test_append_datetime() -> Result<()> { + use crate::params; + use chrono::{NaiveDate, NaiveDateTime}; + + let db = Connection::open_in_memory()?; + db.execute_batch("CREATE TABLE foo(x DATE, y TIMESTAMP)")?; + + let date = NaiveDate::from_ymd_opt(2024, 6, 5).unwrap(); + let timestamp = date.and_hms_opt(18, 26, 53).unwrap(); + { + let mut app = db.appender("foo")?; + app.append_row(params![date, timestamp])?; + } + let (date2, timestamp2) = db.query_row("SELECT x, y FROM foo", [], |row| { + Ok((row.get::<_, NaiveDate>(0)?, row.get::<_, NaiveDateTime>(1)?)) + })?; + assert_eq!(date, date2); + assert_eq!(timestamp, timestamp2); + Ok(()) + } + #[test] fn test_appender_error() -> Result<(), crate::Error> { let conn = Connection::open_in_memory()?;