From b20168fe9cb16217fcea2a3da04fe0f13722097f Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 7 May 2016 12:08:57 +0200 Subject: [PATCH 1/5] Use String::from_utf8_lossy for error/trace. Try to use the original message even if there are invalid characters. --- src/lib.rs | 3 +-- src/trace.rs | 21 +++++++++------------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 930e1c6..466ac78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,8 +101,7 @@ pub type Result = result::Result; unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { let c_slice = CStr::from_ptr(errmsg).to_bytes(); - let utf8_str = str::from_utf8(c_slice); - utf8_str.unwrap_or("Invalid string encoding").to_owned() + String::from_utf8_lossy(c_slice).into_owned() } fn str_to_cstring(s: &str) -> Result { diff --git a/src/trace.rs b/src/trace.rs index a36aa33..7dd1417 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -27,9 +27,8 @@ pub unsafe fn config_log(callback: Option) -> Result<()> { let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() }; let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) }; - if let Ok(s) = str::from_utf8(c_slice) { - callback(err, s); - } + let s = String::from_utf8_lossy(c_slice); + callback(err, &s); } let rc = match callback { @@ -70,9 +69,8 @@ impl Connection { unsafe extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) { let trace_fn: fn(&str) = mem::transmute(p_arg); let c_slice = CStr::from_ptr(z_sql).to_bytes(); - if let Ok(s) = str::from_utf8(c_slice) { - trace_fn(s); - } + let s = String::from_utf8_lossy(c_slice); + trace_fn(&s); } let c = self.db.borrow_mut(); @@ -96,13 +94,12 @@ impl Connection { nanoseconds: u64) { let profile_fn: fn(&str, Duration) = mem::transmute(p_arg); let c_slice = CStr::from_ptr(z_sql).to_bytes(); - if let Ok(s) = str::from_utf8(c_slice) { - const NANOS_PER_SEC: u64 = 1_000_000_000; + let s = String::from_utf8_lossy(c_slice); + const NANOS_PER_SEC: u64 = 1_000_000_000; - let duration = Duration::new(nanoseconds / NANOS_PER_SEC, - (nanoseconds % NANOS_PER_SEC) as u32); - profile_fn(s, duration); - } + let duration = Duration::new(nanoseconds / NANOS_PER_SEC, + (nanoseconds % NANOS_PER_SEC) as u32); + profile_fn(&s, duration); } let c = self.db.borrow_mut(); From 34d5e2db2467cd81fbddaff8ea8996a2f93b8adc Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Mon, 16 May 2016 09:08:31 -0500 Subject: [PATCH 2/5] Always store DateTimes in UTC --- src/types/chrono.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/types/chrono.rs b/src/types/chrono.rs index 98ed77e..dd63c40 100644 --- a/src/types/chrono.rs +++ b/src/types/chrono.rs @@ -91,10 +91,11 @@ impl FromSql for NaiveDateTime { } } -/// Date and time with time zone => RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM"). -impl ToSql for DateTime where Tz::Offset: ::std::fmt::Display { +/// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00"). +impl ToSql for DateTime { unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { - self.to_rfc3339().bind_parameter(stmt, col) + let utc_dt = self.with_timezone(&UTC); + utc_dt.to_rfc3339().bind_parameter(stmt, col) } } @@ -205,9 +206,10 @@ mod test { db.execute("INSERT INTO foo (t) VALUES (?)", &[&local]).unwrap(); + // Stored string should be in UTC let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); - let offset = Local.offset_from_utc_datetime(&dt); - assert_eq!(format!("2016-02-23T23:56:04.789{:}", offset), s); + assert!(s.ends_with("+00:00")); + let v: DateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); assert_eq!(local, v); } From 6d9b268776bb87f9057139c4869b741a1a6999cd Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Mon, 16 May 2016 09:13:45 -0500 Subject: [PATCH 3/5] Add tests confirming DateTime works with " " seperator instead of "T" --- src/types/chrono.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/types/chrono.rs b/src/types/chrono.rs index dd63c40..39a7093 100644 --- a/src/types/chrono.rs +++ b/src/types/chrono.rs @@ -129,7 +129,7 @@ impl FromSql for DateTime { #[cfg(test)] mod test { use Connection; - use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, UTC}; + use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, UTC, Duration}; fn checked_memory_handle() -> Connection { let db = Connection::open_in_memory().unwrap(); @@ -192,8 +192,15 @@ mod test { let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); assert_eq!("2016-02-23T23:56:04.789+00:00", s); - let v: DateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); - assert_eq!(utc, v); + + let v1: DateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); + assert_eq!(utc, v1); + + let v2: DateTime = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0)).unwrap(); + assert_eq!(utc, v2); + + let v3: DateTime = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0)).unwrap(); + assert_eq!(utc - Duration::milliseconds(789), v3); } #[test] From 6a4abff462ff8442a682400831ffa6654a6208d9 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Mon, 16 May 2016 09:27:50 -0500 Subject: [PATCH 4/5] Restore support for full RFC3339 timestamps with a space seperator --- src/types/chrono.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/types/chrono.rs b/src/types/chrono.rs index 39a7093..4aaa63d 100644 --- a/src/types/chrono.rs +++ b/src/types/chrono.rs @@ -102,7 +102,16 @@ impl ToSql for DateTime { /// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime. impl FromSql for DateTime { unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result> { - let s = try!(String::column_result(stmt, col)); + let s = { + let mut s = try!(String::column_result(stmt, col)); + if s.len() >= 11 { + let sbytes = s.as_mut_vec(); + if sbytes[10] == b' ' { + sbytes[10] = b'T'; + } + } + s + }; match DateTime::parse_from_rfc3339(&s) { Ok(dt) => Ok(dt.with_timezone(&UTC)), Err(_) => NaiveDateTime::column_result(stmt, col).map(|dt| UTC.from_utc_datetime(&dt)), @@ -201,6 +210,9 @@ mod test { let v3: DateTime = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0)).unwrap(); assert_eq!(utc - Duration::milliseconds(789), v3); + + let v4: DateTime = db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0)).unwrap(); + assert_eq!(utc, v4); } #[test] From 4924c0b38b134b498ec12f0e58f9eb115837a2dd Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Mon, 16 May 2016 10:02:07 -0500 Subject: [PATCH 5/5] rustfmt --- src/types/chrono.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/types/chrono.rs b/src/types/chrono.rs index 4aaa63d..a9e0bea 100644 --- a/src/types/chrono.rs +++ b/src/types/chrono.rs @@ -68,8 +68,8 @@ impl ToSql for NaiveDateTime { } } -/// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date and time without timezone. -/// ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported) +/// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date and time +/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported) impl FromSql for NaiveDateTime { unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result { let s = try!(String::column_result(stmt, col)); @@ -138,7 +138,8 @@ impl FromSql for DateTime { #[cfg(test)] mod test { use Connection; - use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, UTC, Duration}; + use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, UTC, + Duration}; fn checked_memory_handle() -> Connection { let db = Connection::open_in_memory().unwrap(); @@ -205,13 +206,16 @@ mod test { let v1: DateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); assert_eq!(utc, v1); - let v2: DateTime = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0)).unwrap(); + let v2: DateTime = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0)) + .unwrap(); assert_eq!(utc, v2); - let v3: DateTime = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0)).unwrap(); + let v3: DateTime = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0)) + .unwrap(); assert_eq!(utc - Duration::milliseconds(789), v3); - let v4: DateTime = db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0)).unwrap(); + let v4: DateTime = + db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0)).unwrap(); assert_eq!(utc, v4); }