1use super::{PreparedStatement, ResultRow,
4 ColIx, ParamIx};
5use super::{
6 SqliteResult, SqliteErrorCode, SqliteError,
7};
8use super::ColumnType::SQLITE_NULL;
9
10use time;
11
12pub trait ToSql {
14 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()>;
16}
17
18pub trait FromSql : Sized {
28 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Self>;
30}
31
32impl ToSql for i32 {
33 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
34 s.bind_int(ix, *self)
35 }
36}
37
38impl FromSql for i32 {
39 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<i32> { Ok(row.column_int(col)) }
40}
41
42impl ToSql for i64 {
43 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
44 s.bind_int64(ix, *self)
45 }
46}
47
48impl FromSql for i64 {
49 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<i64> { Ok(row.column_int64(col)) }
50}
51
52impl ToSql for f64 {
53 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
54 s.bind_double(ix, *self)
55 }
56}
57
58impl FromSql for f64 {
59 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<f64> { Ok(row.column_double(col)) }
60}
61
62impl ToSql for bool {
63 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
64 s.bind_int(ix, if *self { 1 } else { 0 })
65 }
66}
67
68impl FromSql for bool {
69 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<bool> { Ok(row.column_int(col)!=0) }
70}
71
72impl<T: ToSql + Clone> ToSql for Option<T> {
73 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
74 match (*self).clone() {
75 Some(x) => x.to_sql(s, ix),
76 None => s.bind_null(ix)
77 }
78 }
79}
80
81impl<T: FromSql + Clone> FromSql for Option<T> {
82 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Option<T>> {
83 match row.column_type(col) {
84 SQLITE_NULL => Ok(None),
85 _ => FromSql::from_sql(row, col).map(|x| Some(x))
86 }
87 }
88}
89
90impl ToSql for String {
91 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
92 s.bind_text(ix, (*self).as_ref())
93 }
94}
95
96
97impl FromSql for String {
98 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<String> {
99 Ok(row.column_text(col).unwrap_or(String::new()))
100 }
101}
102
103impl<'a> ToSql for &'a [u8] {
104 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
105 s.bind_blob(ix, *self)
106 }
107}
108
109impl FromSql for Vec<u8> {
110 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<Vec<u8>> {
111 Ok(row.column_blob(col).unwrap_or(Vec::new()))
112 }
113}
114
115
116pub static SQLITE_TIME_FMT: &'static str = "%F %T";
122
123impl FromSql for time::Tm {
124 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<time::Tm> {
125 let txt = row.column_text(col).unwrap_or(String::new());
126 Ok( try!(time::strptime(txt.as_ref(), SQLITE_TIME_FMT)) )
127 }
128}
129
130impl From<time::ParseError> for SqliteError {
131 fn from(err: time::ParseError) -> SqliteError {
132 SqliteError {
133 kind: SqliteErrorCode::SQLITE_MISMATCH,
134 desc: "Time did not match expected format",
135 detail: Some(format!("Time parsing error: {}", err)),
136 }
137 }
138}
139
140impl ToSql for time::Timespec {
141 fn to_sql(&self, s: &mut PreparedStatement, ix: ParamIx) -> SqliteResult<()> {
142 let timestr = time::at_utc(*self).strftime(SQLITE_TIME_FMT)
143 .unwrap() .to_string();
145 s.bind_text(ix, timestr.as_ref())
146 }
147}
148
149impl FromSql for time::Timespec {
150 fn from_sql(row: &ResultRow, col: ColIx) -> SqliteResult<time::Timespec> {
152 let tmo: SqliteResult<time::Tm> = FromSql::from_sql(row, col);
153 tmo.map(|tm| tm.to_timespec())
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use time::Tm;
160 use super::super::{DatabaseConnection, SqliteResult, ResultSet};
161 use super::super::{ResultRowAccess};
162
163 fn with_query<T, F>(sql: &str, mut f: F) -> SqliteResult<T>
164 where F: FnMut(&mut ResultSet) -> T
165 {
166 let db = try!(DatabaseConnection::in_memory());
167 let mut s = try!(db.prepare(sql));
168 let mut rows = s.execute();
169 Ok(f(&mut rows))
170 }
171
172 #[test]
173 fn get_tm() {
174 fn go() -> SqliteResult<()> {
175 let conn = try!(DatabaseConnection::in_memory());
176 let mut stmt = try!(
177 conn.prepare("select datetime('2001-01-01', 'weekday 3', '3 hours')"));
178 let mut results = stmt.execute();
179 match results.step() {
180 Ok(Some(ref mut row)) => {
181 assert_eq!(
182 row.get::<u32, Tm>(0),
183 Tm { tm_sec: 0,
184 tm_min: 0,
185 tm_hour: 3,
186 tm_mday: 3,
187 tm_mon: 0,
188 tm_year: 101,
189 tm_wday: 0,
190 tm_yday: 0,
191 tm_isdst: 0,
192 tm_utcoff: 0,
193 tm_nsec: 0
194 });
195 Ok(())
196 },
197 Ok(None) => panic!("no row"),
198 Err(oops) => panic!("error: {:?}", oops)
199 }
200 }
201 go().unwrap();
202 }
203
204 #[test]
205 fn get_invalid_tm() {
206 with_query("select 'not a time'", |results| {
207 match results.step() {
208 Ok(Some(ref mut row)) => {
209 let x : SqliteResult<Tm> = row.get_opt(0u32);
210 assert!(x.is_err());
211 },
212 Ok(None) => panic!("no row"),
213 Err(oops) => panic!("error: {:?}", oops)
214 };
215 }).unwrap();
216 }
217
218 #[test]
219 fn select_blob() {
220 with_query("select x'ff0db0'", |results| {
221 match results.step() {
222 Ok(Some(ref mut row)) => {
223 let x : SqliteResult<Vec<u8>> = row.get_opt(0u32);
224 assert_eq!(x.ok().unwrap(), [0xff, 0x0d, 0xb0].to_vec());
225 },
226 Ok(None) => panic!("no row"),
227 Err(oops) => panic!("error: {:?}", oops)
228 };
229 }).unwrap();
230 }
231}
232
233