1use ffi;
2use libc::{c_double, c_int};
3use std::marker::PhantomData;
4
5use {Cursor, Result, Type, Value};
6
7macro_rules! transient(
9 () => (::std::mem::transmute(!0 as *const ::libc::c_void));
10);
11
12pub struct Statement<'l> {
14 raw: (*mut ffi::sqlite3_stmt, *mut ffi::sqlite3),
15 phantom: PhantomData<(ffi::sqlite3_stmt, &'l ffi::sqlite3)>,
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum State {
21 Row,
23 Done,
25}
26
27pub trait Bindable {
29 fn bind(self, &mut Statement, usize) -> Result<()>;
33}
34
35pub trait Readable: Sized {
37 fn read(&Statement, usize) -> Result<Self>;
41}
42
43impl<'l> Statement<'l> {
44 #[inline]
48 pub fn bind<T: Bindable>(&mut self, i: usize, value: T) -> Result<()> {
49 value.bind(self, i)
50 }
51
52 #[inline]
54 pub fn count(&self) -> usize {
55 unsafe { ffi::sqlite3_column_count(self.raw.0) as usize }
56 }
57
58 pub fn kind(&self, i: usize) -> Type {
62 debug_assert!(i < self.count(), "the index is out of range");
63 match unsafe { ffi::sqlite3_column_type(self.raw.0, i as c_int) } {
64 ffi::SQLITE_BLOB => Type::Binary,
65 ffi::SQLITE_FLOAT => Type::Float,
66 ffi::SQLITE_INTEGER => Type::Integer,
67 ffi::SQLITE_TEXT => Type::String,
68 ffi::SQLITE_NULL => Type::Null,
69 _ => unreachable!(),
70 }
71 }
72
73 #[inline]
75 pub fn name(&self, i: usize) -> &str {
76 debug_assert!(i < self.count(), "the index is out of range");
77 unsafe {
78 let pointer = ffi::sqlite3_column_name(self.raw.0, i as c_int);
79 debug_assert!(!pointer.is_null());
80 c_str_to_str!(pointer).unwrap()
81 }
82 }
83
84 #[inline]
86 pub fn names(&self) -> Vec<&str> {
87 (0..self.count()).map(|i| self.name(i)).collect()
88 }
89
90 pub fn next(&mut self) -> Result<State> {
95 Ok(match unsafe { ffi::sqlite3_step(self.raw.0) } {
96 ffi::SQLITE_ROW => State::Row,
97 ffi::SQLITE_DONE => State::Done,
98 code => error!(self.raw.1, code),
99 })
100 }
101
102 #[inline]
106 pub fn read<T: Readable>(&self, i: usize) -> Result<T> {
107 debug_assert!(i < self.count(), "the index is out of range");
108 Readable::read(self, i)
109 }
110
111 #[inline]
113 pub fn reset(&mut self) -> Result<()> {
114 unsafe { ok!(self.raw.1, ffi::sqlite3_reset(self.raw.0)) };
115 Ok(())
116 }
117
118 #[inline]
120 pub fn cursor(self) -> Cursor<'l> {
121 ::cursor::new(self)
122 }
123
124 #[inline]
126 pub fn as_raw(&self) -> *mut ffi::sqlite3_stmt {
127 self.raw.0
128 }
129}
130
131impl<'l> Drop for Statement<'l> {
132 #[inline]
133 fn drop(&mut self) {
134 unsafe { ffi::sqlite3_finalize(self.raw.0) };
135 }
136}
137
138impl<'l> Bindable for &'l Value {
139 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
140 match self {
141 &Value::Binary(ref value) => (value as &[u8]).bind(statement, i),
142 &Value::Float(value) => value.bind(statement, i),
143 &Value::Integer(value) => value.bind(statement, i),
144 &Value::String(ref value) => (value as &str).bind(statement, i),
145 &Value::Null => ().bind(statement, i),
146 }
147 }
148}
149
150impl<'l> Bindable for &'l [u8] {
151 #[inline]
152 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
153 debug_assert!(i > 0, "the indexing starts from 1");
154 unsafe {
155 ok!(
156 statement.raw.1,
157 ffi::sqlite3_bind_blob(
158 statement.raw.0,
159 i as c_int,
160 self.as_ptr() as *const _,
161 self.len() as c_int,
162 transient!(),
163 )
164 );
165 }
166 Ok(())
167 }
168}
169
170impl Bindable for f64 {
171 #[inline]
172 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
173 debug_assert!(i > 0, "the indexing starts from 1");
174 unsafe {
175 ok!(
176 statement.raw.1,
177 ffi::sqlite3_bind_double(statement.raw.0, i as c_int, self as c_double)
178 );
179 }
180 Ok(())
181 }
182}
183
184impl Bindable for i64 {
185 #[inline]
186 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
187 debug_assert!(i > 0, "the indexing starts from 1");
188 unsafe {
189 ok!(
190 statement.raw.1,
191 ffi::sqlite3_bind_int64(statement.raw.0, i as c_int, self as ffi::sqlite3_int64)
192 );
193 }
194 Ok(())
195 }
196}
197
198impl<'l> Bindable for &'l str {
199 #[inline]
200 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
201 debug_assert!(i > 0, "the indexing starts from 1");
202 unsafe {
203 ok!(
204 statement.raw.1,
205 ffi::sqlite3_bind_text(
206 statement.raw.0,
207 i as c_int,
208 self.as_ptr() as *const _,
209 self.len() as c_int,
210 transient!(),
211 )
212 );
213 }
214 Ok(())
215 }
216}
217
218impl Bindable for () {
219 #[inline]
220 fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
221 debug_assert!(i > 0, "the indexing starts from 1");
222 unsafe {
223 ok!(
224 statement.raw.1,
225 ffi::sqlite3_bind_null(statement.raw.0, i as c_int)
226 );
227 }
228 Ok(())
229 }
230}
231
232impl Readable for Value {
233 fn read(statement: &Statement, i: usize) -> Result<Self> {
234 Ok(match statement.kind(i) {
235 Type::Binary => Value::Binary(try!(Readable::read(statement, i))),
236 Type::Float => Value::Float(try!(Readable::read(statement, i))),
237 Type::Integer => Value::Integer(try!(Readable::read(statement, i))),
238 Type::String => Value::String(try!(Readable::read(statement, i))),
239 Type::Null => Value::Null,
240 })
241 }
242}
243
244impl Readable for f64 {
245 #[inline]
246 fn read(statement: &Statement, i: usize) -> Result<Self> {
247 Ok(unsafe { ffi::sqlite3_column_double(statement.raw.0, i as c_int) as f64 })
248 }
249}
250
251impl Readable for i64 {
252 #[inline]
253 fn read(statement: &Statement, i: usize) -> Result<Self> {
254 Ok(unsafe { ffi::sqlite3_column_int64(statement.raw.0, i as c_int) as i64 })
255 }
256}
257
258impl Readable for String {
259 #[inline]
260 fn read(statement: &Statement, i: usize) -> Result<Self> {
261 unsafe {
262 let pointer = ffi::sqlite3_column_text(statement.raw.0, i as c_int);
263 if pointer.is_null() {
264 raise!("cannot read a text column");
265 }
266 Ok(c_str_to_string!(pointer))
267 }
268 }
269}
270
271impl Readable for Vec<u8> {
272 #[inline]
273 fn read(statement: &Statement, i: usize) -> Result<Self> {
274 use std::ptr::copy_nonoverlapping as copy;
275 unsafe {
276 let pointer = ffi::sqlite3_column_blob(statement.raw.0, i as c_int);
277 if pointer.is_null() {
278 return Ok(vec![]);
279 }
280 let count = ffi::sqlite3_column_bytes(statement.raw.0, i as c_int) as usize;
281 let mut buffer = Vec::with_capacity(count);
282 buffer.set_len(count);
283 copy(pointer as *const u8, buffer.as_mut_ptr(), count);
284 Ok(buffer)
285 }
286 }
287}
288
289#[inline]
290pub fn new<'l, T: AsRef<str>>(raw1: *mut ffi::sqlite3, statement: T) -> Result<Statement<'l>> {
291 let mut raw0 = 0 as *mut _;
292 unsafe {
293 ok!(
294 raw1,
295 ffi::sqlite3_prepare_v2(
296 raw1,
297 str_to_cstr!(statement.as_ref()).as_ptr(),
298 -1,
299 &mut raw0,
300 0 as *mut _,
301 )
302 );
303 }
304 Ok(Statement {
305 raw: (raw0, raw1),
306 phantom: PhantomData,
307 })
308}