fce_sqlite_connector/
lib.rs

1//! Interface to [SQLite][1].
2//!
3//! ## Example
4//!
5//! Open a connection, create a table, and insert some rows:
6//!
7//! ```
8//! let connection = sqlite::open(":memory:").unwrap();
9//!
10//! connection
11//!     .execute(
12//!         "
13//!         CREATE TABLE users (name TEXT, age INTEGER);
14//!         INSERT INTO users VALUES ('Alice', 42);
15//!         INSERT INTO users VALUES ('Bob', 69);
16//!         ",
17//!     )
18//!     .unwrap();
19//! ```
20//!
21//! Select some rows and process them one by one as plain text:
22//!
23//! ```
24//! # let connection = sqlite::open(":memory:").unwrap();
25//! # connection
26//! #     .execute(
27//! #         "
28//! #         CREATE TABLE users (name TEXT, age INTEGER);
29//! #         INSERT INTO users VALUES ('Alice', 42);
30//! #         INSERT INTO users VALUES ('Bob', 69);
31//! #         ",
32//! #     )
33//! #     .unwrap();
34//! connection
35//!     .iterate("SELECT * FROM users WHERE age > 50", |pairs| {
36//!         for &(column, value) in pairs.iter() {
37//!             println!("{} = {}", column, value.unwrap());
38//!         }
39//!         true
40//!     })
41//!     .unwrap();
42//! ```
43//!
44//! The same query using a prepared statement, which is much more efficient than
45//! the previous technique:
46//!
47//! ```
48//! use sqlite::State;
49//! # let connection = sqlite::open(":memory:").unwrap();
50//! # connection
51//! #     .execute(
52//! #         "
53//! #         CREATE TABLE users (name TEXT, age INTEGER);
54//! #         INSERT INTO users VALUES ('Alice', 42);
55//! #         INSERT INTO users VALUES ('Bob', 69);
56//! #         ",
57//! #     )
58//! #     .unwrap();
59//!
60//! let mut statement = connection
61//!     .prepare("SELECT * FROM users WHERE age > ?")
62//!     .unwrap();
63//!
64//! statement.bind(1, 50).unwrap();
65//!
66//! while let State::Row = statement.next().unwrap() {
67//!     println!("name = {}", statement.read::<String>(0).unwrap());
68//!     println!("age = {}", statement.read::<i64>(1).unwrap());
69//! }
70//! ```
71//!
72//! The same query using a cursor, which is a wrapper around a prepared
73//! statement providing the concept of row and featuring all-at-once binding:
74//!
75//! ```
76//! use sqlite::Value;
77//! # let connection = sqlite::open(":memory:").unwrap();
78//! # connection
79//! #     .execute(
80//! #         "
81//! #         CREATE TABLE users (name TEXT, age INTEGER);
82//! #         INSERT INTO users VALUES ('Alice', 42);
83//! #         INSERT INTO users VALUES ('Bob', 69);
84//! #         ",
85//! #     )
86//! #     .unwrap();
87//!
88//! let mut cursor = connection
89//!     .prepare("SELECT * FROM users WHERE age > ?")
90//!     .unwrap()
91//!     .cursor();
92//!
93//! cursor.bind(&[Value::Integer(50)]).unwrap();
94//!
95//! while let Some(row) = cursor.next().unwrap() {
96//!     println!("name = {}", row[0].as_string().unwrap());
97//!     println!("age = {}", row[1].as_integer().unwrap());
98//! }
99//! ```
100//!
101//! [1]: https://www.sqlite.org
102
103#![allow(dead_code)]
104
105use sqlite3_connector as ffi;
106
107use std::{error, fmt};
108
109macro_rules! error(
110    ($connection:expr, $code:expr) => (
111        match ::last_error($connection) {
112            Some(error) => return Err(error),
113            _ => return Err(::Error {
114                code: Some($code as isize),
115                message: None,
116            }),
117        }
118    );
119);
120
121macro_rules! ok_descr(
122    ($connection:expr, $result:expr) => (
123        match $result.ret_code {
124            ::ffi::SQLITE_OK => {}
125            code => error!($connection, code),
126        }
127    );
128    ($result:expr) => (
129        match $result.ret_code {
130            ::ffi::SQLITE_OK => {}
131            code => return Err(::Error {
132                code: Some(code as isize),
133                message: None,
134            }),
135        }
136    );
137);
138
139macro_rules! ok_raw(
140    ($connection:expr, $result:expr) => (
141        match $result {
142            ::ffi::SQLITE_OK => {}
143            code => error!($connection, code),
144        }
145    );
146    ($result:expr) => (
147        match $result {
148            ::ffi::SQLITE_OK => {}
149            code => return Err(::Error {
150                code: Some(code as isize),
151                message: None,
152            }),
153        }
154    );
155);
156
157/// An error.
158#[derive(Debug)]
159pub struct Error {
160    /// The error code.
161    pub code: Option<isize>,
162    /// The error message.
163    pub message: Option<String>,
164}
165
166/// A result.
167pub type Result<T> = std::result::Result<T, Error>;
168
169/// A data type.
170#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171pub enum Type {
172    /// The binary type.
173    Binary,
174    /// The floating-point type.
175    Float,
176    /// The integer type.
177    Integer,
178    /// The string type.
179    String,
180    /// The null type.
181    Null,
182}
183
184/// A typed value.
185#[derive(Clone, Debug, PartialEq)]
186pub enum Value {
187    /// Binary data.
188    Binary(Vec<u8>),
189    /// A floating-point number.
190    Float(f64),
191    /// An integer number.
192    Integer(i64),
193    /// A string.
194    String(String),
195    /// A null value.
196    Null,
197}
198
199impl fmt::Display for Error {
200    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
201        match (self.code, &self.message) {
202            (Some(code), &Some(ref message)) => write!(formatter, "{} (code {})", message, code),
203            (Some(code), _) => write!(formatter, "an SQLite error (code {})", code),
204            (_, &Some(ref message)) => message.fmt(formatter),
205            _ => write!(formatter, "an SQLite error"),
206        }
207    }
208}
209
210impl error::Error for Error {
211    fn description(&self) -> &str {
212        match self.message {
213            Some(ref message) => message,
214            _ => "an SQLite error",
215        }
216    }
217}
218
219impl Value {
220    /// Return the binary data if the value is `Binary`.
221    #[inline]
222    pub fn as_binary(&self) -> Option<&[u8]> {
223        if let &Value::Binary(ref value) = self {
224            return Some(value);
225        }
226        None
227    }
228
229    /// Return the floating-point number if the value is `Float`.
230    #[inline]
231    pub fn as_float(&self) -> Option<f64> {
232        if let &Value::Float(value) = self {
233            return Some(value);
234        }
235        None
236    }
237
238    /// Return the integer number if the value is `Integer`.
239    #[inline]
240    pub fn as_integer(&self) -> Option<i64> {
241        if let &Value::Integer(value) = self {
242            return Some(value);
243        }
244        None
245    }
246
247    /// Return the string if the value is `String`.
248    #[inline]
249    pub fn as_string(&self) -> Option<&str> {
250        if let &Value::String(ref value) = self {
251            return Some(value);
252        }
253        None
254    }
255
256    /// Return the type.
257    pub fn kind(&self) -> Type {
258        match self {
259            &Value::Binary(_) => Type::Binary,
260            &Value::Float(_) => Type::Float,
261            &Value::Integer(_) => Type::Integer,
262            &Value::String(_) => Type::String,
263            &Value::Null => Type::Null,
264        }
265    }
266}
267
268mod connection;
269mod cursor;
270mod sqlite3_connector;
271mod statement;
272
273pub use connection::Connection;
274pub use connection::OpenFlags;
275pub use cursor::Cursor;
276pub use statement::{Bindable, Readable, State, Statement};
277pub use sqlite3_connector::*;
278
279/// Open a read-write connection to a new or existing database.
280#[inline]
281pub fn open<T: AsRef<std::path::Path>>(path: T) -> Result<Connection> {
282    Connection::open(path)
283}
284
285/// Return the version number of SQLite.
286///
287/// For instance, the version `3.8.11.1` corresponds to the integer `3008011`.
288#[inline]
289pub fn version() -> usize {
290    unsafe { ffi::sqlite3_libversion_number() as usize }
291}
292
293fn last_error(raw: ffi::Sqlite3DbHandle) -> Option<Error> {
294    unsafe {
295        let code = ffi::sqlite3_errcode(raw);
296        if code == ffi::SQLITE_OK {
297            return None;
298        }
299        let message = ffi::sqlite3_errmsg(raw);
300        Some(Error {
301            code: Some(code as isize),
302            message: Some(message),
303        })
304    }
305}