Skip to content

Commit f48a4e3

Browse files
authored
Implement appender for date/time types (#313)
* Implement appender for date/time types * Remove unused import * Add unit test for date/time append
1 parent 3bd503c commit f48a4e3

File tree

2 files changed

+51
-8
lines changed

2 files changed

+51
-8
lines changed

crates/duckdb/src/appender/mod.rs

+39-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{ffi::c_void, fmt, os::raw::c_char};
33

44
use crate::{
55
error::result_from_duckdb_appender,
6-
types::{TimeUnit, ToSql, ToSqlOutput},
6+
types::{ToSql, ToSqlOutput},
77
Error,
88
};
99

@@ -118,15 +118,23 @@ impl Appender<'_> {
118118
ffi::duckdb_append_varchar_length(ptr, s.as_ptr() as *const c_char, s.len() as u64)
119119
},
120120
ValueRef::Timestamp(u, i) => unsafe {
121-
let micros = match u {
122-
TimeUnit::Second => i * 1_000_000,
123-
TimeUnit::Millisecond => i * 1_000,
124-
TimeUnit::Microsecond => i,
125-
TimeUnit::Nanosecond => i / 1_000,
126-
};
127-
ffi::duckdb_append_timestamp(ptr, ffi::duckdb_timestamp { micros })
121+
ffi::duckdb_append_timestamp(ptr, ffi::duckdb_timestamp { micros: u.to_micros(i) })
128122
},
129123
ValueRef::Blob(b) => unsafe { ffi::duckdb_append_blob(ptr, b.as_ptr() as *const c_void, b.len() as u64) },
124+
ValueRef::Date32(d) => unsafe { ffi::duckdb_append_date(ptr, ffi::duckdb_date { days: d }) },
125+
ValueRef::Time64(u, v) => unsafe {
126+
ffi::duckdb_append_time(ptr, ffi::duckdb_time { micros: u.to_micros(v) })
127+
},
128+
ValueRef::Interval { months, days, nanos } => unsafe {
129+
ffi::duckdb_append_interval(
130+
ptr,
131+
ffi::duckdb_interval {
132+
months,
133+
days,
134+
micros: nanos / 1000,
135+
},
136+
)
137+
},
130138
_ => unreachable!("not supported"),
131139
};
132140
if rc != 0 {
@@ -255,6 +263,29 @@ mod test {
255263
Ok(())
256264
}
257265

266+
#[test]
267+
#[cfg(feature = "chrono")]
268+
fn test_append_datetime() -> Result<()> {
269+
use crate::params;
270+
use chrono::{NaiveDate, NaiveDateTime};
271+
272+
let db = Connection::open_in_memory()?;
273+
db.execute_batch("CREATE TABLE foo(x DATE, y TIMESTAMP)")?;
274+
275+
let date = NaiveDate::from_ymd_opt(2024, 6, 5).unwrap();
276+
let timestamp = date.and_hms_opt(18, 26, 53).unwrap();
277+
{
278+
let mut app = db.appender("foo")?;
279+
app.append_row(params![date, timestamp])?;
280+
}
281+
let (date2, timestamp2) = db.query_row("SELECT x, y FROM foo", [], |row| {
282+
Ok((row.get::<_, NaiveDate>(0)?, row.get::<_, NaiveDateTime>(1)?))
283+
})?;
284+
assert_eq!(date, date2);
285+
assert_eq!(timestamp, timestamp2);
286+
Ok(())
287+
}
288+
258289
#[test]
259290
fn test_appender_error() -> Result<(), crate::Error> {
260291
let conn = Connection::open_in_memory()?;

crates/duckdb/src/types/value_ref.rs

+12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ pub enum TimeUnit {
2323
Nanosecond,
2424
}
2525

26+
impl TimeUnit {
27+
/// Convert a number of `TimeUnit` to microseconds.
28+
pub fn to_micros(&self, value: i64) -> i64 {
29+
match self {
30+
TimeUnit::Second => value * 1_000_000,
31+
TimeUnit::Millisecond => value * 1000,
32+
TimeUnit::Microsecond => value,
33+
TimeUnit::Nanosecond => value / 1000,
34+
}
35+
}
36+
}
37+
2638
/// A non-owning [static type value](https://duckdb.org/docs/sql/data_types/overview). Typically the
2739
/// memory backing this value is owned by SQLite.
2840
///

0 commit comments

Comments
 (0)