Merge branch 'master' into gwenn-json

This commit is contained in:
John Gallagher 2016-05-16 10:14:09 -05:00
commit b47b644246
3 changed files with 46 additions and 25 deletions

View File

@ -101,8 +101,7 @@ pub type Result<T> = result::Result<T, Error>;
unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { unsafe fn errmsg_to_string(errmsg: *const c_char) -> String {
let c_slice = CStr::from_ptr(errmsg).to_bytes(); let c_slice = CStr::from_ptr(errmsg).to_bytes();
let utf8_str = str::from_utf8(c_slice); String::from_utf8_lossy(c_slice).into_owned()
utf8_str.unwrap_or("Invalid string encoding").to_owned()
} }
fn str_to_cstring(s: &str) -> Result<CString> { fn str_to_cstring(s: &str) -> Result<CString> {

View File

@ -27,9 +27,8 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() }; let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() };
let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) }; let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) };
if let Ok(s) = str::from_utf8(c_slice) { let s = String::from_utf8_lossy(c_slice);
callback(err, s); callback(err, &s);
}
} }
let rc = match callback { 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) { 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 trace_fn: fn(&str) = mem::transmute(p_arg);
let c_slice = CStr::from_ptr(z_sql).to_bytes(); let c_slice = CStr::from_ptr(z_sql).to_bytes();
if let Ok(s) = str::from_utf8(c_slice) { let s = String::from_utf8_lossy(c_slice);
trace_fn(s); trace_fn(&s);
}
} }
let c = self.db.borrow_mut(); let c = self.db.borrow_mut();
@ -96,13 +94,12 @@ impl Connection {
nanoseconds: u64) { nanoseconds: u64) {
let profile_fn: fn(&str, Duration) = mem::transmute(p_arg); let profile_fn: fn(&str, Duration) = mem::transmute(p_arg);
let c_slice = CStr::from_ptr(z_sql).to_bytes(); let c_slice = CStr::from_ptr(z_sql).to_bytes();
if let Ok(s) = str::from_utf8(c_slice) { let s = String::from_utf8_lossy(c_slice);
const NANOS_PER_SEC: u64 = 1_000_000_000; const NANOS_PER_SEC: u64 = 1_000_000_000;
let duration = Duration::new(nanoseconds / NANOS_PER_SEC, let duration = Duration::new(nanoseconds / NANOS_PER_SEC,
(nanoseconds % NANOS_PER_SEC) as u32); (nanoseconds % NANOS_PER_SEC) as u32);
profile_fn(s, duration); profile_fn(&s, duration);
}
} }
let c = self.db.borrow_mut(); let c = self.db.borrow_mut();

View File

@ -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-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date and time
/// ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported) /// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported)
impl FromSql for NaiveDateTime { impl FromSql for NaiveDateTime {
unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<NaiveDateTime> { unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<NaiveDateTime> {
let s = try!(String::column_result(stmt, col)); let s = try!(String::column_result(stmt, col));
@ -91,17 +91,27 @@ impl FromSql for NaiveDateTime {
} }
} }
/// Date and time with time zone => RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM"). /// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
impl<Tz: TimeZone> ToSql for DateTime<Tz> where Tz::Offset: ::std::fmt::Display { impl<Tz: TimeZone> ToSql for DateTime<Tz> {
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { 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)
} }
} }
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<UTC>. /// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<UTC>.
impl FromSql for DateTime<UTC> { impl FromSql for DateTime<UTC> {
unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<DateTime<UTC>> { unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<DateTime<UTC>> {
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) { match DateTime::parse_from_rfc3339(&s) {
Ok(dt) => Ok(dt.with_timezone(&UTC)), Ok(dt) => Ok(dt.with_timezone(&UTC)),
Err(_) => NaiveDateTime::column_result(stmt, col).map(|dt| UTC.from_utc_datetime(&dt)), Err(_) => NaiveDateTime::column_result(stmt, col).map(|dt| UTC.from_utc_datetime(&dt)),
@ -128,7 +138,8 @@ impl FromSql for DateTime<Local> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use Connection; 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 { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
@ -191,8 +202,21 @@ mod test {
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); 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); assert_eq!("2016-02-23T23:56:04.789+00:00", s);
let v: DateTime<UTC> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
assert_eq!(utc, v); let v1: DateTime<UTC> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
assert_eq!(utc, v1);
let v2: DateTime<UTC> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v2);
let v3: DateTime<UTC> = 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<UTC> =
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0)).unwrap();
assert_eq!(utc, v4);
} }
#[test] #[test]
@ -205,9 +229,10 @@ mod test {
db.execute("INSERT INTO foo (t) VALUES (?)", &[&local]).unwrap(); 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 s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
let offset = Local.offset_from_utc_datetime(&dt); assert!(s.ends_with("+00:00"));
assert_eq!(format!("2016-02-23T23:56:04.789{:}", offset), s);
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
assert_eq!(local, v); assert_eq!(local, v);
} }