├── .gitignore ├── Makefile ├── benches ├── common │ └── mod.rs ├── statement.rs └── cursor.rs ├── .github └── workflows │ └── build.yml ├── tests ├── common │ └── mod.rs ├── cursor.rs ├── connection.rs ├── row.rs └── statement.rs ├── Cargo.toml ├── LICENSE.md ├── src ├── error.rs ├── lib.rs ├── value.rs ├── cursor.rs ├── connection.rs └── statement.rs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite3 2 | Cargo.lock 3 | target 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: 3 | 4 | .PHONY: publish 5 | publish: 6 | git tag --force v$$(cat Cargo.toml | grep version | head -1 | cut -d\" -f2) 7 | git push --tags 8 | cargo publish 9 | -------------------------------------------------------------------------------- /benches/common/mod.rs: -------------------------------------------------------------------------------- 1 | use sqlite::{Connection, State}; 2 | 3 | macro_rules! ok(($result:expr) => ($result.unwrap())); 4 | 5 | pub fn create() -> Connection { 6 | let connection = ok!(Connection::open(":memory:")); 7 | let query = "CREATE TABLE data (a INTEGER, b REAL, c REAL, d REAL)"; 8 | ok!(connection.execute(query)); 9 | connection 10 | } 11 | 12 | pub fn populate(connection: &Connection, count: usize) { 13 | let query = "INSERT INTO data (a, b, c, d) VALUES (?, ?, ?, ?)"; 14 | let mut statement = ok!(connection.prepare(query)); 15 | for i in 1..(count + 1) { 16 | ok!(statement.reset()); 17 | ok!(statement.bind((1, i as i64))); 18 | ok!(statement.bind((2, i as f64))); 19 | ok!(statement.bind((3, i as f64))); 20 | ok!(statement.bind((4, i as f64))); 21 | assert_eq!(ok!(statement.next()), State::Done); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | workflow_dispatch: 11 | 12 | jobs: 13 | check: 14 | runs-on: macos-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - run: rustup toolchain install stable --profile=minimal --component=clippy --component=rustfmt 18 | - run: cargo clippy -- -D warnings 19 | - run: cargo fmt --all -- --check 20 | 21 | bench: 22 | strategy: 23 | matrix: 24 | os: [macos-latest, ubuntu-latest, windows-latest] 25 | runs-on: ${{ matrix.os }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | - run: rustup toolchain install nightly --profile=minimal 29 | - run: cargo +nightly bench 30 | 31 | test: 32 | strategy: 33 | matrix: 34 | os: [macos-latest, ubuntu-latest, windows-latest] 35 | runs-on: ${{ matrix.os }} 36 | steps: 37 | - uses: actions/checkout@v4 38 | - run: rustup toolchain install stable --profile=minimal 39 | - run: cargo test --features=bundled 40 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use sqlite::Connection; 4 | use std::path::Path; 5 | 6 | macro_rules! ok(($result:expr) => ($result.unwrap())); 7 | 8 | pub fn setup_english>(path: T) -> Connection { 9 | let connection = ok!(sqlite::open(path)); 10 | ok!(connection.execute( 11 | " 12 | CREATE TABLE english (value TEXT); 13 | INSERT INTO english VALUES ('cerotype'); 14 | INSERT INTO english VALUES ('metatype'); 15 | INSERT INTO english VALUES ('ozotype'); 16 | INSERT INTO english VALUES ('phenotype'); 17 | INSERT INTO english VALUES ('plastotype'); 18 | INSERT INTO english VALUES ('undertype'); 19 | INSERT INTO english VALUES ('nonsence'); 20 | ", 21 | )); 22 | connection 23 | } 24 | 25 | pub fn setup_users>(path: T) -> Connection { 26 | let connection = ok!(sqlite::open(path)); 27 | ok!(connection.execute( 28 | " 29 | CREATE TABLE users (id INTEGER, name TEXT, age REAL, photo BLOB, email TEXT); 30 | INSERT INTO users VALUES (1, 'Alice', 42.69, X'4269', NULL); 31 | ", 32 | )); 33 | connection 34 | } 35 | -------------------------------------------------------------------------------- /benches/statement.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | mod common; 6 | 7 | use sqlite::State; 8 | use test::Bencher; 9 | 10 | use common::{create, populate}; 11 | 12 | macro_rules! ok(($result:expr) => ($result.unwrap())); 13 | 14 | #[bench] 15 | fn read(bencher: &mut Bencher) { 16 | let connection = create(); 17 | populate(&connection, 100); 18 | let query = "SELECT * FROM data WHERE a > ? AND b > ?"; 19 | let mut statement = ok!(connection.prepare(query)); 20 | 21 | bencher.iter(|| { 22 | ok!(statement.reset()); 23 | ok!(statement.bind((1, 42))); 24 | ok!(statement.bind((2, 42.0))); 25 | let mut count = 0; 26 | while let State::Row = ok!(statement.next()) { 27 | assert!(ok!(statement.read::(0)) > 42); 28 | assert!(ok!(statement.read::(1)) > 42.0); 29 | count += 1; 30 | } 31 | assert_eq!(count, 100 - 42); 32 | }) 33 | } 34 | 35 | #[bench] 36 | fn write(bencher: &mut Bencher) { 37 | let connection = create(); 38 | let query = "INSERT INTO data (a, b, c, d) VALUES (?, ?, ?, ?)"; 39 | let mut statement = ok!(connection.prepare(query)); 40 | 41 | bencher.iter(|| { 42 | ok!(statement.reset()); 43 | ok!(statement.bind((1, 42))); 44 | ok!(statement.bind((2, 42.0))); 45 | ok!(statement.bind((3, 42.0))); 46 | ok!(statement.bind((4, 42.0))); 47 | assert_eq!(ok!(statement.next()), State::Done); 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sqlite" 3 | version = "0.37.0" 4 | edition = "2021" 5 | license = "Apache-2.0 OR MIT" 6 | authors = [ 7 | "Alec Moskvin ", 8 | "Angel Leon ", 9 | "Cecile Tonglet ", 10 | "Daniel Dulaney ", 11 | "Eugenio Tampieri ", 12 | "Ivan Stankovic ", 13 | "Ivan Ukhov ", 14 | "Jake Kerr ", 15 | "James Haywood ", 16 | "Jayson Reis ", 17 | "Jonatan Lemes ", 18 | "Luna Lux ", 19 | "Pierre Krieger ", 20 | "Sean Klein ", 21 | "Sophie Tauchert <999eagle@999eagle.moe>", 22 | "Tomoki Aonuma ", 23 | "Vincenzo Palazzo ", 24 | "Yorhel ", 25 | "kodeschreiber", 26 | ] 27 | description = "The package provides an interface to SQLite." 28 | documentation = "https://docs.rs/sqlite" 29 | homepage = "https://github.com/stainless-steel/sqlite" 30 | repository = "https://github.com/stainless-steel/sqlite" 31 | readme = "README.md" 32 | categories = ["api-bindings", "database"] 33 | keywords = ["database"] 34 | 35 | [features] 36 | default = ["linkage"] 37 | bundled = ["sqlite3-sys/bundled"] 38 | extension = [] 39 | encryption = ["sqlite3-sys/encryption"] 40 | linkage = ["sqlite3-sys/linkage"] 41 | 42 | [dependencies.sqlite3-sys] 43 | version = "0.18" 44 | default-features = false 45 | 46 | [dev-dependencies] 47 | temporary = "0.7" 48 | tokio = { version = "1", features = ["macros", "rt-multi-thread"] } 49 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | The project is dual licensed under the terms of the Apache License, Version 2.0, 4 | and the MIT License. You may obtain copies of the two licenses at 5 | 6 | * https://www.apache.org/licenses/LICENSE-2.0 and 7 | * https://opensource.org/licenses/MIT, respectively. 8 | 9 | The following two notices apply to every file of the project. 10 | 11 | ## The Apache License 12 | 13 | ``` 14 | Copyright 2015–2025 The sqlite Developers 15 | 16 | Licensed under the Apache License, Version 2.0 (the “License”); you may not use 17 | this file except in compliance with the License. You may obtain a copy of the 18 | License at 19 | 20 | http://www.apache.org/licenses/LICENSE-2.0 21 | 22 | Unless required by applicable law or agreed to in writing, software distributed 23 | under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 24 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 25 | specific language governing permissions and limitations under the License. 26 | ``` 27 | 28 | ## The MIT License 29 | 30 | ``` 31 | Copyright 2015–2025 The sqlite Developers 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy of 34 | this software and associated documentation files (the “Software”), to deal in 35 | the Software without restriction, including without limitation the rights to 36 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 37 | the Software, and to permit persons to whom the Software is furnished to do so, 38 | subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 45 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 46 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 47 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | ``` 50 | -------------------------------------------------------------------------------- /benches/cursor.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | mod common; 6 | 7 | use sqlite::Value; 8 | use test::Bencher; 9 | 10 | use common::{create, populate}; 11 | 12 | macro_rules! ok(($result:expr) => ($result.unwrap())); 13 | 14 | #[bench] 15 | fn read_next(bencher: &mut Bencher) { 16 | let connection = create(); 17 | populate(&connection, 100); 18 | let query = "SELECT * FROM data WHERE a > ? AND b > ?"; 19 | let mut statement = ok!(connection.prepare(query)); 20 | 21 | bencher.iter(|| { 22 | let mut count = 0; 23 | for row in statement 24 | .iter() 25 | .bind::<&[Value]>(&[42.into(), 42.0.into()][..]) 26 | .unwrap() 27 | .map(|row| row.unwrap()) 28 | { 29 | assert!(row.read::(0) > 42); 30 | assert!(row.read::(1) > 42.0); 31 | count += 1; 32 | } 33 | assert_eq!(count, 100 - 42); 34 | }) 35 | } 36 | 37 | #[bench] 38 | fn read_try_next(bencher: &mut Bencher) { 39 | let connection = create(); 40 | populate(&connection, 100); 41 | let query = "SELECT * FROM data WHERE a > ? AND b > ?"; 42 | let mut statement = ok!(connection.prepare(query)); 43 | 44 | bencher.iter(|| { 45 | let mut cursor = statement 46 | .iter() 47 | .bind::<&[Value]>(&[42.into(), 42.0.into()][..]) 48 | .unwrap(); 49 | let mut count = 0; 50 | while let Ok(Some(row)) = cursor.try_next() { 51 | assert!(ok!((&row[0]).try_into::()) > 42); 52 | assert!(ok!((&row[1]).try_into::()) > 42.0); 53 | count += 1; 54 | } 55 | assert_eq!(count, 100 - 42); 56 | }) 57 | } 58 | 59 | #[bench] 60 | fn write(bencher: &mut Bencher) { 61 | let connection = create(); 62 | let query = "INSERT INTO data (a, b, c, d) VALUES (?, ?, ?, ?)"; 63 | let mut statement = ok!(connection.prepare(query)); 64 | 65 | bencher.iter(|| { 66 | let mut cursor = statement 67 | .iter() 68 | .bind::<&[Value]>(&[42.into(), 42.0.into(), 42.0.into(), 42.0.into()][..]) 69 | .unwrap(); 70 | match cursor.next() { 71 | None => {} 72 | _ => unreachable!(), 73 | } 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fmt}; 2 | 3 | /// An error. 4 | #[derive(Debug)] 5 | pub struct Error { 6 | /// The error code. 7 | pub code: Option, 8 | /// The error message. 9 | pub message: Option, 10 | } 11 | 12 | /// A result. 13 | pub type Result = std::result::Result; 14 | 15 | macro_rules! error( 16 | ($connection:expr, $code:expr) => ( 17 | match crate::error::last($connection) { 18 | Some(error) => return Err(error), 19 | _ => return Err(crate::error::Error { 20 | code: Some($code as isize), 21 | message: None, 22 | }), 23 | } 24 | ); 25 | ); 26 | 27 | macro_rules! ok( 28 | ($connection:expr, $result:expr) => ( 29 | match $result { 30 | crate::ffi::SQLITE_OK => {} 31 | code => error!($connection, code), 32 | } 33 | ); 34 | ($result:expr) => ( 35 | match $result { 36 | crate::ffi::SQLITE_OK => {} 37 | code => return Err(crate::error::Error { 38 | code: Some(code as isize), 39 | message: None, 40 | }), 41 | } 42 | ); 43 | ); 44 | 45 | macro_rules! raise( 46 | ($message:expr $(, $($token:tt)* )?) => ( 47 | return Err(crate::error::Error { 48 | code: None, 49 | message: Some(format!($message $(, $($token)* )*)), 50 | }) 51 | ); 52 | ); 53 | 54 | impl fmt::Display for Error { 55 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 56 | match (self.code, &self.message) { 57 | (Some(code), Some(message)) => write!(formatter, "{message} (code {code})"), 58 | (Some(code), _) => write!(formatter, "an SQLite error (code {code})"), 59 | (_, Some(message)) => message.fmt(formatter), 60 | _ => write!(formatter, "an SQLite error"), 61 | } 62 | } 63 | } 64 | 65 | impl error::Error for Error { 66 | fn description(&self) -> &str { 67 | match self.message { 68 | Some(ref message) => message, 69 | _ => "an SQLite error", 70 | } 71 | } 72 | } 73 | 74 | pub fn last(raw: *mut ffi::sqlite3) -> Option { 75 | unsafe { 76 | let code = ffi::sqlite3_errcode(raw); 77 | if code == ffi::SQLITE_OK { 78 | return None; 79 | } 80 | let message = ffi::sqlite3_errmsg(raw); 81 | if message.is_null() { 82 | return None; 83 | } 84 | Some(Error { 85 | code: Some(code as isize), 86 | message: Some(c_str_to_string!(message)), 87 | }) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLite [![Package][package-img]][package-url] [![Documentation][documentation-img]][documentation-url] [![Build][build-img]][build-url] 2 | 3 | The package provides an interface to [SQLite]. 4 | 5 | ## Example 6 | 7 | Open a connection, create a table, and insert a few rows: 8 | 9 | ```rust 10 | let connection = sqlite::open(":memory:").unwrap(); 11 | 12 | let query = " 13 | CREATE TABLE users (name TEXT, age INTEGER); 14 | INSERT INTO users VALUES ('Alice', 42); 15 | INSERT INTO users VALUES ('Bob', 69); 16 | "; 17 | connection.execute(query).unwrap(); 18 | ``` 19 | 20 | Select some rows and process them one by one as plain text, which is generally 21 | not efficient: 22 | 23 | ```rust 24 | let query = "SELECT * FROM users WHERE age > 50"; 25 | 26 | connection 27 | .iterate(query, |pairs| { 28 | for &(name, value) in pairs.iter() { 29 | println!("{} = {}", name, value.unwrap()); 30 | } 31 | true 32 | }) 33 | .unwrap(); 34 | ``` 35 | 36 | Run the same query but using a prepared statement, which is much more efficient 37 | than the previous technique: 38 | 39 | ```rust 40 | use sqlite::State; 41 | 42 | let query = "SELECT * FROM users WHERE age > ?"; 43 | let mut statement = connection.prepare(query).unwrap(); 44 | statement.bind((1, 50)).unwrap(); 45 | 46 | while let Ok(State::Row) = statement.next() { 47 | println!("name = {}", statement.read::("name").unwrap()); 48 | println!("age = {}", statement.read::("age").unwrap()); 49 | } 50 | ``` 51 | 52 | Run the same query but using a cursor, which is iterable: 53 | 54 | ```rust 55 | let query = "SELECT * FROM users WHERE age > ?"; 56 | 57 | for row in connection 58 | .prepare(query) 59 | .unwrap() 60 | .into_iter() 61 | .bind((1, 50)) 62 | .unwrap() 63 | .map(|row| row.unwrap()) 64 | { 65 | println!("name = {}", row.read::<&str, _>("name")); 66 | println!("age = {}", row.read::("age")); 67 | } 68 | ``` 69 | 70 | ## Contribution 71 | 72 | Your contribution is highly appreciated. Do not hesitate to open an issue or a 73 | pull request. Note that any contribution submitted for inclusion in the project 74 | will be licensed according to the terms given in [LICENSE.md](LICENSE.md). 75 | 76 | [SQLite]: https://www.sqlite.org 77 | 78 | [build-img]: https://github.com/stainless-steel/sqlite/workflows/build/badge.svg 79 | [build-url]: https://github.com/stainless-steel/sqlite/actions/workflows/build.yml 80 | [documentation-img]: https://docs.rs/sqlite/badge.svg 81 | [documentation-url]: https://docs.rs/sqlite 82 | [package-img]: https://img.shields.io/crates/v/sqlite.svg 83 | [package-url]: https://crates.io/crates/sqlite 84 | -------------------------------------------------------------------------------- /tests/cursor.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use sqlite::{Type, Value}; 4 | 5 | mod common; 6 | 7 | use common::{setup_english, setup_users}; 8 | 9 | macro_rules! ok(($result:expr) => ($result.unwrap())); 10 | 11 | #[test] 12 | fn bind_iter() { 13 | let connection = ok!(sqlite::open(":memory:")); 14 | ok!(connection.execute("CREATE TABLE users (id INTEGER, name STRING)")); 15 | let mut statement = ok!(connection.prepare("INSERT INTO users VALUES (:id, :name)")); 16 | 17 | let mut map = HashMap::<_, Value>::new(); 18 | map.insert(":name", "Bob".to_string().into()); 19 | map.insert(":id", 42.into()); 20 | 21 | let mut cursor = ok!(statement.iter().bind_iter(map)); 22 | assert!(cursor.next().is_none()); 23 | } 24 | 25 | #[test] 26 | fn iter() { 27 | let connection = setup_users(":memory:"); 28 | ok!(connection.execute("INSERT INTO users VALUES (2, 'Bob', NULL, NULL, NULL)")); 29 | let query = "SELECT id, age FROM users ORDER BY 1 DESC"; 30 | let mut statement = ok!(connection.prepare(query)); 31 | 32 | let mut count = 0; 33 | for row in statement.iter().map(|row| ok!(row)) { 34 | let id = row.read::("id"); 35 | if id == 1 { 36 | assert_eq!(row.read::("age"), 42.69); 37 | } else if id == 2 { 38 | assert_eq!(row.read::, _>("age"), None); 39 | } else { 40 | assert!(false); 41 | } 42 | count += 1; 43 | } 44 | assert_eq!(count, 2); 45 | } 46 | 47 | #[test] 48 | fn iter_column_count() { 49 | let connection = setup_english(":memory:"); 50 | let query = "SELECT value FROM english WHERE value LIKE '%type'"; 51 | let mut statement = ok!(connection.prepare(query)); 52 | 53 | let cursor = statement.iter(); 54 | assert_eq!(cursor.column_count(), 1); 55 | } 56 | 57 | #[test] 58 | fn iter_column_type() { 59 | let connection = setup_english(":memory:"); 60 | let query = "SELECT value FROM english WHERE value LIKE '%type'"; 61 | let mut statement = ok!(connection.prepare(query)); 62 | 63 | let cursor = statement.iter(); 64 | assert_eq!(ok!(cursor.column_type(0)), Type::Null); 65 | assert_eq!(ok!(cursor.column_type("value")), Type::Null); 66 | 67 | ok!(statement.reset()); 68 | let mut cursor = statement.iter(); 69 | ok!(cursor.try_next()); 70 | assert_eq!(ok!(cursor.column_type(0)), Type::String); 71 | assert_eq!(ok!(cursor.column_type("value")), Type::String); 72 | 73 | ok!(statement.reset()); 74 | let mut count = 0; 75 | let mut cursor = statement.iter(); 76 | while let Ok(Some(_)) = cursor.try_next() { 77 | assert_eq!(ok!(cursor.column_type(0)), Type::String); 78 | assert_eq!(ok!(cursor.column_type("value")), Type::String); 79 | count += 1; 80 | } 81 | assert_eq!(count, 6); 82 | } 83 | 84 | #[test] 85 | fn iter_count() { 86 | let connection = setup_english(":memory:"); 87 | let query = "SELECT value FROM english WHERE value LIKE '%type'"; 88 | let mut statement = ok!(connection.prepare(query)); 89 | 90 | assert_eq!(statement.iter().filter(|row| row.is_ok()).count(), 6); 91 | } 92 | 93 | #[test] 94 | fn iter_with_exception() { 95 | let connection = ok!(sqlite::open(":memory:")); 96 | ok!(connection.execute("CREATE TABLE foo(x)")); 97 | ok!(connection 98 | .execute("CREATE TRIGGER bar BEFORE INSERT ON foo BEGIN SELECT RAISE(FAIL, 'buz'); END")); 99 | let mut statement = ok!(connection.prepare("INSERT INTO foo VALUES (0) RETURNING rowid;")); 100 | let results = statement.iter().collect::>(); 101 | assert_eq!(results.len(), 1); 102 | assert!(matches!(results[0], Err(_))); 103 | } 104 | 105 | #[test] 106 | fn workflow() { 107 | let connection = setup_users(":memory:"); 108 | 109 | let select = "SELECT id, name FROM users WHERE id = ?"; 110 | let mut select = ok!(connection.prepare(select)); 111 | let mut select = select.iter(); 112 | 113 | let insert = "INSERT INTO users (id, name) VALUES (?, ?)"; 114 | let mut insert = ok!(connection.prepare(insert)); 115 | let insert = insert.iter(); 116 | 117 | for _ in 0..10 { 118 | select = ok!(select.bind((1, 1))); 119 | let row = ok!(ok!(select.next())); 120 | assert_eq!(row.read::("id"), 1); 121 | assert_eq!(row.read::<&str, _>("name"), "Alice"); 122 | assert!(select.next().is_none()); 123 | } 124 | 125 | let mut select = ok!(select.bind((1, 42))); 126 | assert!(select.next().is_none()); 127 | 128 | let mut insert = ok!(insert.bind::<&[Value]>(&[42.into(), String::from("Bob").into()][..])); 129 | assert!(insert.next().is_none()); 130 | 131 | let mut select = ok!(select.bind((1, 42))); 132 | let row = ok!(ok!(select.next())); 133 | assert_eq!(row.read::("id"), 42); 134 | assert_eq!(row.read::<&str, _>("name"), "Bob"); 135 | assert!(select.next().is_none()); 136 | } 137 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Interface to [SQLite][1]. 2 | //! 3 | //! ## Example 4 | //! 5 | //! Open a connection, create a table, and insert a few rows: 6 | //! 7 | //! ``` 8 | //! let connection = sqlite::open(":memory:").unwrap(); 9 | //! 10 | //! let query = " 11 | //! CREATE TABLE users (name TEXT, age INTEGER); 12 | //! INSERT INTO users VALUES ('Alice', 42); 13 | //! INSERT INTO users VALUES ('Bob', 69); 14 | //! "; 15 | //! connection.execute(query).unwrap(); 16 | //! ``` 17 | //! 18 | //! Select some rows and process them one by one as plain text, which is generally not efficient: 19 | //! 20 | //! ``` 21 | //! # let connection = sqlite::open(":memory:").unwrap(); 22 | //! # let query = " 23 | //! # CREATE TABLE users (name TEXT, age INTEGER); 24 | //! # INSERT INTO users VALUES ('Alice', 42); 25 | //! # INSERT INTO users VALUES ('Bob', 69); 26 | //! # "; 27 | //! # connection.execute(query).unwrap(); 28 | //! let query = "SELECT * FROM users WHERE age > 50"; 29 | //! 30 | //! connection 31 | //! .iterate(query, |pairs| { 32 | //! for &(name, value) in pairs.iter() { 33 | //! println!("{} = {}", name, value.unwrap()); 34 | //! } 35 | //! true 36 | //! }) 37 | //! .unwrap(); 38 | //! ``` 39 | //! 40 | //! Run the same query but using a prepared statement, which is much more efficient than the 41 | //! previous technique: 42 | //! 43 | //! ``` 44 | //! use sqlite::State; 45 | //! # let connection = sqlite::open(":memory:").unwrap(); 46 | //! # let query = " 47 | //! # CREATE TABLE users (name TEXT, age INTEGER); 48 | //! # INSERT INTO users VALUES ('Alice', 42); 49 | //! # INSERT INTO users VALUES ('Bob', 69); 50 | //! # "; 51 | //! # connection.execute(query).unwrap(); 52 | //! 53 | //! let query = "SELECT * FROM users WHERE age > ?"; 54 | //! let mut statement = connection.prepare(query).unwrap(); 55 | //! statement.bind((1, 50)).unwrap(); 56 | //! 57 | //! while let Ok(State::Row) = statement.next() { 58 | //! println!("name = {}", statement.read::("name").unwrap()); 59 | //! println!("age = {}", statement.read::("age").unwrap()); 60 | //! } 61 | //! ``` 62 | //! 63 | //! Run the same query but using a cursor, which is iterable: 64 | //! 65 | //! ``` 66 | //! # let connection = sqlite::open(":memory:").unwrap(); 67 | //! # let query = " 68 | //! # CREATE TABLE users (name TEXT, age INTEGER); 69 | //! # INSERT INTO users VALUES ('Alice', 42); 70 | //! # INSERT INTO users VALUES ('Bob', 69); 71 | //! # "; 72 | //! # connection.execute(query).unwrap(); 73 | //! 74 | //! let query = "SELECT * FROM users WHERE age > ?"; 75 | //! 76 | //! for row in connection 77 | //! .prepare(query) 78 | //! .unwrap() 79 | //! .into_iter() 80 | //! .bind((1, 50)) 81 | //! .unwrap() 82 | //! .map(|row| row.unwrap()) 83 | //! { 84 | //! println!("name = {}", row.read::<&str, _>("name")); 85 | //! println!("age = {}", row.read::("age")); 86 | //! } 87 | //! ``` 88 | //! 89 | //! [1]: https://www.sqlite.org 90 | 91 | pub extern crate sqlite3_sys as ffi; 92 | 93 | macro_rules! c_str_to_str( 94 | ($string:expr) => (std::str::from_utf8(std::ffi::CStr::from_ptr($string).to_bytes())); 95 | ); 96 | 97 | macro_rules! c_str_to_string( 98 | ($string:expr) => ( 99 | String::from_utf8_lossy(std::ffi::CStr::from_ptr($string as *const _).to_bytes()) 100 | .into_owned() 101 | ); 102 | ); 103 | 104 | macro_rules! path_to_cstr( 105 | ($path:expr) => ( 106 | match $path.to_str() { 107 | Some(path) => { 108 | match std::ffi::CString::new(path) { 109 | Ok(string) => string, 110 | _ => raise!("failed to process a path"), 111 | } 112 | } 113 | _ => raise!("failed to process a path"), 114 | } 115 | ); 116 | ); 117 | 118 | macro_rules! str_to_cstr( 119 | ($string:expr) => ( 120 | match std::ffi::CString::new($string) { 121 | Ok(string) => string, 122 | _ => raise!("failed to process a string"), 123 | } 124 | ); 125 | ); 126 | 127 | #[macro_use] 128 | mod error; 129 | mod value; 130 | 131 | mod connection; 132 | mod cursor; 133 | mod statement; 134 | 135 | pub use error::{Error, Result}; 136 | pub use value::{Type, Value}; 137 | 138 | pub use connection::{Connection, ConnectionThreadSafe, OpenFlags}; 139 | pub use cursor::{Cursor, CursorWithOwnership, Row, RowIndex}; 140 | pub use statement::{ 141 | Bindable, BindableWithIndex, ColumnIndex, ParameterIndex, ReadableWithIndex, State, Statement, 142 | }; 143 | 144 | /// Open a read-write connection to a new or existing database. 145 | #[inline] 146 | pub fn open>(path: T) -> Result { 147 | Connection::open(path) 148 | } 149 | 150 | /// Return the version number of SQLite. 151 | /// 152 | /// For instance, the version `3.8.11.1` corresponds to the integer `3008011`. 153 | #[inline] 154 | pub fn version() -> usize { 155 | unsafe { ffi::sqlite3_libversion_number() as usize } 156 | } 157 | -------------------------------------------------------------------------------- /tests/connection.rs: -------------------------------------------------------------------------------- 1 | use sqlite::{Connection, OpenFlags, State}; 2 | 3 | mod common; 4 | 5 | use common::setup_users; 6 | 7 | macro_rules! ok(($result:expr) => ($result.unwrap())); 8 | 9 | #[test] 10 | fn open_with_flags() { 11 | use temporary::Folder; 12 | 13 | let path = ok!(Folder::new("sqlite")); 14 | let path = path.path().join("database.sqlite3"); 15 | setup_users(&path); 16 | 17 | let flags = OpenFlags::new().with_read_only(); 18 | let connection = ok!(Connection::open_with_flags(path, flags)); 19 | match connection.execute("INSERT INTO users VALUES (2, 'Bob', NULL, NULL)") { 20 | Err(_) => {} 21 | _ => unreachable!(), 22 | } 23 | } 24 | 25 | #[tokio::test] 26 | async fn open_thread_safe_async() { 27 | use std::sync::Arc; 28 | 29 | use tokio::task::spawn_blocking as spawn; 30 | 31 | let connection = Arc::new(ok!(ok!( 32 | spawn(|| Connection::open_thread_safe(":memory:")).await 33 | ))); 34 | 35 | { 36 | let connection = connection.clone(); 37 | ok!(ok!(spawn(move || connection.execute("SELECT 1")).await)); 38 | } 39 | 40 | { 41 | let connection = connection.clone(); 42 | ok!(ok!(spawn(move || connection.execute("SELECT 1")).await)); 43 | } 44 | } 45 | 46 | #[test] 47 | fn open_thread_safe_sync() { 48 | use std::sync::Arc; 49 | use std::thread::spawn; 50 | 51 | let connection = Arc::new(ok!(Connection::open_thread_safe(":memory:"))); 52 | 53 | let mut threads = Vec::new(); 54 | for _ in 0..5 { 55 | let connection = connection.clone(); 56 | threads.push(spawn(move || { 57 | ok!(connection.execute("SELECT 1")); 58 | })); 59 | } 60 | for thread in threads { 61 | ok!(thread.join()); 62 | } 63 | } 64 | 65 | #[test] 66 | fn execute() { 67 | let connection = setup_users(":memory:"); 68 | match connection.execute(":)") { 69 | Err(error) => assert_eq!( 70 | error.message, 71 | Some(String::from(r#"unrecognized token: ":""#)) 72 | ), 73 | _ => unreachable!(), 74 | } 75 | } 76 | 77 | #[test] 78 | fn iterate() { 79 | macro_rules! pair( 80 | ($one:expr, $two:expr) => (($one, Some($two))); 81 | ); 82 | 83 | let connection = setup_users(":memory:"); 84 | 85 | let mut done = false; 86 | let query = "SELECT * FROM users"; 87 | ok!(connection.iterate(query, |pairs| { 88 | assert_eq!(pairs.len(), 5); 89 | assert_eq!(pairs[0], pair!("id", "1")); 90 | assert_eq!(pairs[1], pair!("name", "Alice")); 91 | assert_eq!(pairs[2], pair!("age", "42.69")); 92 | assert_eq!(pairs[3], pair!("photo", "\x42\x69")); 93 | assert_eq!(pairs[4], ("email", None)); 94 | done = true; 95 | true 96 | })); 97 | assert!(done); 98 | } 99 | 100 | #[test] 101 | fn set_busy_handler() { 102 | use std::thread::spawn; 103 | use temporary::Folder; 104 | 105 | let path = ok!(Folder::new("sqlite")); 106 | let path = path.path().join("database.sqlite3"); 107 | setup_users(&path); 108 | 109 | let guards = (0..10) 110 | .map(|_| { 111 | let path = path.to_path_buf(); 112 | spawn(move || { 113 | let mut connection = ok!(sqlite::open(&path)); 114 | ok!(connection.set_busy_handler(|_| true)); 115 | let query = "INSERT INTO users VALUES (?, ?, ?, ?, ?)"; 116 | let mut statement = ok!(connection.prepare(query)); 117 | ok!(statement.bind((1, 2i64))); 118 | ok!(statement.bind((2, "Bob"))); 119 | ok!(statement.bind((3, 69.42))); 120 | ok!(statement.bind((4, &[0x69u8, 0x42u8][..]))); 121 | ok!(statement.bind((5, ()))); 122 | assert_eq!(ok!(statement.next()), State::Done); 123 | true 124 | }) 125 | }) 126 | .collect::>(); 127 | 128 | for guard in guards { 129 | assert!(ok!(guard.join())); 130 | } 131 | } 132 | 133 | #[cfg(feature = "extension")] 134 | #[test] 135 | fn enable_extension() { 136 | let connection = ok!(Connection::open(":memory:")); 137 | ok!(connection.enable_extension()); 138 | } 139 | 140 | #[cfg(feature = "extension")] 141 | #[test] 142 | fn disable_extension() { 143 | let connection = ok!(Connection::open(":memory:")); 144 | ok!(connection.disable_extension()); 145 | } 146 | 147 | #[cfg(feature = "extension")] 148 | #[test] 149 | fn load_extension() { 150 | let connection = ok!(Connection::open(":memory:")); 151 | ok!(connection.enable_extension()); 152 | assert!(connection.load_extension("libsqlitefunctions").is_err()); 153 | } 154 | 155 | #[test] 156 | fn change_count() { 157 | let connection = setup_users(":memory:"); 158 | assert_eq!(connection.change_count(), 1); 159 | assert_eq!(connection.total_change_count(), 1); 160 | 161 | ok!(connection.execute("INSERT INTO users VALUES (2, 'Bob', NULL, NULL, NULL)")); 162 | assert_eq!(connection.change_count(), 1); 163 | assert_eq!(connection.total_change_count(), 2); 164 | 165 | ok!(connection.execute("UPDATE users SET name = 'Bob' WHERE id = 1")); 166 | assert_eq!(connection.change_count(), 1); 167 | assert_eq!(connection.total_change_count(), 3); 168 | 169 | ok!(connection.execute("DELETE FROM users")); 170 | assert_eq!(connection.change_count(), 2); 171 | assert_eq!(connection.total_change_count(), 5); 172 | } 173 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | use crate::error::{Error, Result}; 4 | 5 | /// A value. 6 | #[derive(Clone, Debug, Default, PartialEq)] 7 | pub enum Value { 8 | /// Binary data. 9 | Binary(Vec), 10 | /// A floating-point number. 11 | Float(f64), 12 | /// An integer number. 13 | Integer(i64), 14 | /// A string. 15 | String(String), 16 | /// A null value. 17 | #[default] 18 | Null, 19 | } 20 | 21 | /// The type of a value. 22 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 23 | pub enum Type { 24 | /// The binary type. 25 | Binary, 26 | /// The floating-point type. 27 | Float, 28 | /// The integer type. 29 | Integer, 30 | /// The string type. 31 | String, 32 | /// The null type. 33 | Null, 34 | } 35 | 36 | impl Value { 37 | /// Return the type. 38 | pub fn kind(&self) -> Type { 39 | match self { 40 | Value::Binary(_) => Type::Binary, 41 | Value::Float(_) => Type::Float, 42 | Value::Integer(_) => Type::Integer, 43 | Value::String(_) => Type::String, 44 | Value::Null => Type::Null, 45 | } 46 | } 47 | 48 | /// Try to return the value. 49 | #[inline] 50 | pub fn try_into<'l, T>(&'l self) -> Result 51 | where 52 | T: TryFrom<&'l Value, Error = Error>, 53 | { 54 | T::try_from(self) 55 | } 56 | } 57 | 58 | macro_rules! implement( 59 | ($type:ty, Null) => { 60 | impl From<$type> for Value { 61 | #[inline] 62 | fn from(_: $type) -> Self { 63 | Value::Null 64 | } 65 | } 66 | }; 67 | ($type:ty, $value:ident) => { 68 | impl From<$type> for Value { 69 | #[inline] 70 | fn from(value: $type) -> Self { 71 | Value::$value(value.into()) 72 | } 73 | } 74 | }; 75 | ); 76 | 77 | implement!(Vec, Binary); 78 | implement!(&[u8], Binary); 79 | implement!(f64, Float); 80 | implement!(i64, Integer); 81 | implement!(String, String); 82 | implement!(&str, String); 83 | implement!((), Null); 84 | 85 | macro_rules! implement( 86 | (@value $type:ty, $value:ident) => { 87 | impl TryFrom for $type { 88 | type Error = Error; 89 | 90 | #[inline] 91 | fn try_from(value: Value) -> Result { 92 | if let Value::$value(value) = value { 93 | return Ok(value); 94 | } 95 | raise!("failed to convert"); 96 | } 97 | } 98 | 99 | impl TryFrom for Option<$type> { 100 | type Error = Error; 101 | 102 | #[inline] 103 | fn try_from(value: Value) -> Result { 104 | if let Value::Null = value { 105 | return Ok(None); 106 | } 107 | <$type>::try_from(value).and_then(|value| Ok(Some(value))) 108 | } 109 | } 110 | }; 111 | (@reference (), Null) => { 112 | impl TryFrom<&Value> for () { 113 | type Error = Error; 114 | 115 | #[inline] 116 | fn try_from(value: &Value) -> Result { 117 | if let &Value::Null = value { 118 | return Ok(()); 119 | } 120 | raise!("failed to convert"); 121 | } 122 | } 123 | }; 124 | (@reference $type:ty, $value:ident) => { 125 | impl TryFrom<&Value> for $type { 126 | type Error = Error; 127 | 128 | #[inline] 129 | fn try_from(value: &Value) -> Result { 130 | if let &Value::$value(value) = value { 131 | return Ok(value); 132 | } 133 | raise!("failed to convert"); 134 | } 135 | } 136 | 137 | impl TryFrom<&Value> for Option<$type> { 138 | type Error = Error; 139 | 140 | #[inline] 141 | fn try_from(value: &Value) -> Result { 142 | if let &Value::Null = value { 143 | return Ok(None); 144 | } 145 | <$type>::try_from(value).and_then(|value| Ok(Some(value))) 146 | } 147 | } 148 | }; 149 | (@reference-lifetime $type:ty, $value:ident) => { 150 | impl<'l> TryFrom<&'l Value> for $type { 151 | type Error = Error; 152 | 153 | #[inline] 154 | fn try_from(value: &'l Value) -> Result { 155 | if let &Value::$value(ref value) = value { 156 | return Ok(value); 157 | } 158 | raise!("failed to convert"); 159 | } 160 | } 161 | 162 | impl<'l> TryFrom<&'l Value> for Option<$type> { 163 | type Error = Error; 164 | 165 | #[inline] 166 | fn try_from(value: &'l Value) -> Result { 167 | if let Value::Null = value { 168 | return Ok(None); 169 | } 170 | <$type>::try_from(value).and_then(|value| Ok(Some(value))) 171 | } 172 | } 173 | }; 174 | ); 175 | 176 | implement!(@value Vec, Binary); 177 | implement!(@reference-lifetime &'l [u8], Binary); 178 | implement!(@reference f64, Float); 179 | implement!(@reference i64, Integer); 180 | implement!(@value String, String); 181 | implement!(@reference-lifetime &'l str, String); 182 | implement!(@reference (), Null); 183 | 184 | impl From> for Value 185 | where 186 | T: Into, 187 | Value: From, 188 | { 189 | #[inline] 190 | fn from(value: Option) -> Self { 191 | match value { 192 | Some(value) => Value::from(value), 193 | _ => Self::Null, 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /tests/row.rs: -------------------------------------------------------------------------------- 1 | use sqlite::Value; 2 | 3 | mod common; 4 | 5 | use common::setup_users; 6 | 7 | macro_rules! ok(($result:expr) => ($result.unwrap())); 8 | 9 | #[test] 10 | fn index() { 11 | let connection = setup_users(":memory:"); 12 | let query = "SELECT * FROM users"; 13 | let mut statement = ok!(connection.prepare(query)); 14 | 15 | let row = ok!(ok!(statement.iter().next())); 16 | 17 | assert_eq!(row[0], Value::Integer(1)); 18 | assert_eq!(row[2], Value::Float(42.69)); 19 | 20 | assert_eq!(row["id"], Value::Integer(1)); 21 | assert_eq!(row["age"], Value::Float(42.69)); 22 | } 23 | 24 | #[test] 25 | fn iter_count() { 26 | let connection = setup_users(":memory:"); 27 | let query = "SELECT * FROM users"; 28 | let mut statement = ok!(connection.prepare(query)); 29 | let mut cursor = statement.iter(); 30 | let row = ok!(ok!(cursor.next())); 31 | let row = row.iter(); 32 | assert_eq!(5, row.count()); 33 | } 34 | 35 | #[test] 36 | fn iter_order() { 37 | let connection = setup_users(":memory:"); 38 | let query = "SELECT * FROM users"; 39 | let mut statement = ok!(connection.prepare(query)); 40 | let mut cursor = statement.iter(); 41 | let row = ok!(ok!(cursor.next())); 42 | let mut row = row.iter(); 43 | assert_eq!(Some(("id", &Value::Integer(1))), row.next()); 44 | assert_eq!( 45 | Some(("name", &Value::String("Alice".to_owned()))), 46 | row.next(), 47 | ); 48 | assert_eq!(Some(("age", &Value::Float(42.69))), row.next()); 49 | assert_eq!(Some(("photo", &Value::Binary(vec![66, 105]))), row.next()); 50 | assert_eq!(Some(("email", &Value::Null)), row.next()); 51 | assert_eq!(None, row.next()); 52 | } 53 | 54 | #[test] 55 | fn read_with_name() { 56 | let connection = setup_users(":memory:"); 57 | let query = "SELECT * FROM users"; 58 | let mut statement = ok!(connection.prepare(query)); 59 | 60 | let row = ok!(ok!(statement.iter().next())); 61 | assert_eq!(row.read::("id"), 1); 62 | assert_eq!(row.read::<&str, _>("name"), "Alice"); 63 | assert_eq!(row.read::("age"), 42.69); 64 | assert_eq!(row.read::<&[u8], _>("photo"), &[0x42u8, 0x69u8][..]); 65 | } 66 | 67 | #[test] 68 | fn read_with_name_and_option() { 69 | let connection = setup_users(":memory:"); 70 | let query = "SELECT * FROM users"; 71 | let mut statement = ok!(connection.prepare(query)); 72 | 73 | let row = ok!(ok!(statement.iter().next())); 74 | assert!(row.read::, _>("id").is_some()); 75 | assert!(row.read::, _>("name").is_some()); 76 | assert!(row.read::, _>("age").is_some()); 77 | assert!(row.read::, _>("photo").is_some()); 78 | assert!(row.read::, _>("email").is_none()); 79 | } 80 | 81 | #[test] 82 | fn take() { 83 | let connection = setup_users(":memory:"); 84 | let query = "SELECT * FROM users"; 85 | let mut statement = ok!(connection.prepare(query)); 86 | 87 | let mut row = ok!(ok!(statement.iter().next())); 88 | assert_eq!(row.take("name"), Value::String("Alice".into())); 89 | assert_eq!(row.take("name"), Value::Null); 90 | } 91 | 92 | #[test] 93 | fn try_read_with_index() { 94 | let connection = setup_users(":memory:"); 95 | let query = "SELECT * FROM users"; 96 | let mut statement = ok!(connection.prepare(query)); 97 | 98 | let row = ok!(ok!(statement.iter().next())); 99 | assert!(row.try_read::(0).is_err()); 100 | assert!(row.try_read::(0).is_ok()); 101 | assert!(row.try_read::<&str, _>(1).is_ok()); 102 | assert!(row.try_read::(2).is_ok()); 103 | assert!(row.try_read::<&[u8], _>(3).is_ok()); 104 | assert!(row.try_read::<&str, _>(4).is_err()); 105 | } 106 | 107 | #[test] 108 | fn try_read_with_index_out_of_range() { 109 | let connection = setup_users(":memory:"); 110 | let query = "SELECT * FROM users"; 111 | let mut statement = ok!(connection.prepare(query)); 112 | 113 | let row = ok!(ok!(statement.iter().next())); 114 | assert!(row.try_read::<&str, _>(5).is_err()); 115 | } 116 | 117 | #[test] 118 | fn try_read_with_index_and_option() { 119 | let connection = setup_users(":memory:"); 120 | let query = "SELECT * FROM users"; 121 | let mut statement = ok!(connection.prepare(query)); 122 | 123 | let row = ok!(ok!(statement.iter().next())); 124 | assert!(row.try_read::, _>(0).is_err()); 125 | assert!(ok!(row.try_read::, _>(0)).is_some()); 126 | assert!(ok!(row.try_read::, _>(1)).is_some()); 127 | assert!(ok!(row.try_read::, _>(2)).is_some()); 128 | assert!(ok!(row.try_read::, _>(3)).is_some()); 129 | assert!(ok!(row.try_read::, _>(4)).is_none()); 130 | } 131 | 132 | #[test] 133 | fn try_read_with_name() { 134 | let connection = setup_users(":memory:"); 135 | let query = "SELECT * FROM users"; 136 | let mut statement = ok!(connection.prepare(query)); 137 | 138 | let row = ok!(ok!(statement.iter().next())); 139 | assert!(row.try_read::("id").is_err()); 140 | assert!(row.try_read::("id").is_ok()); 141 | assert!(row.try_read::<&str, _>("name").is_ok()); 142 | assert!(row.try_read::("age").is_ok()); 143 | assert!(row.try_read::<&[u8], _>("photo").is_ok()); 144 | assert!(row.try_read::<&str, _>("email").is_err()); 145 | } 146 | 147 | #[test] 148 | fn try_read_with_name_and_option() { 149 | let connection = setup_users(":memory:"); 150 | let query = "SELECT * FROM users"; 151 | let mut statement = ok!(connection.prepare(query)); 152 | 153 | let row = ok!(ok!(statement.iter().next())); 154 | assert!(row.try_read::, _>("id").is_err()); 155 | assert!(ok!(row.try_read::, _>("id")).is_some()); 156 | assert!(ok!(row.try_read::, _>("name")).is_some()); 157 | assert!(ok!(row.try_read::, _>("age")).is_some()); 158 | assert!(ok!(row.try_read::, _>("photo")).is_some()); 159 | assert!(ok!(row.try_read::, _>("email")).is_none()); 160 | } 161 | 162 | #[test] 163 | fn try_into() { 164 | let connection = setup_users(":memory:"); 165 | let query = "SELECT * FROM users"; 166 | let mut statement = ok!(connection.prepare(query)); 167 | 168 | let mut cursor = statement.iter(); 169 | let row = ok!(ok!(cursor.try_next())); 170 | assert!((&row[0]).try_into::().is_err()); 171 | assert!((&row[0]).try_into::().is_ok()); 172 | assert!((&row[1]).try_into::<&str>().is_ok()); 173 | assert!((&row[2]).try_into::().is_ok()); 174 | assert!((&row[3]).try_into::<&[u8]>().is_ok()); 175 | assert!((&row[4]).try_into::<&str>().is_err()); 176 | } 177 | -------------------------------------------------------------------------------- /src/cursor.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::convert::TryFrom; 3 | use std::ops::{Deref, Index}; 4 | use std::rc::Rc; 5 | 6 | use crate::error::{Error, Result}; 7 | use crate::statement::{Bindable, State, Statement}; 8 | use crate::value::Value; 9 | 10 | /// An iterator for a prepared statement. 11 | pub struct Cursor<'l, 'm> { 12 | column_count: usize, 13 | statement: &'m mut Statement<'l>, 14 | poisoned: bool, 15 | } 16 | 17 | /// An iterator for a prepared statement with ownership. 18 | pub struct CursorWithOwnership<'l> { 19 | column_count: usize, 20 | statement: Statement<'l>, 21 | poisoned: bool, 22 | } 23 | 24 | /// A row. 25 | #[derive(Debug)] 26 | pub struct Row { 27 | column_names: Rc>, 28 | column_mapping: Rc>, 29 | values: Vec, 30 | } 31 | 32 | /// A type suitable for indexing columns in a row. 33 | pub trait RowIndex: std::fmt::Debug + std::fmt::Display { 34 | /// Check if the index is present in a row. 35 | fn contains(&self, row: &Row) -> bool; 36 | 37 | /// Identify the ordinal position. 38 | /// 39 | /// The first column has index 0. 40 | fn index(self, row: &Row) -> usize; 41 | } 42 | 43 | macro_rules! implement( 44 | ($type:ident<$($lifetime:lifetime),+>) => { 45 | impl<$($lifetime),+> $type<$($lifetime),+> { 46 | /// Bind values to parameters. 47 | /// 48 | /// In case of integer indices, the first parameter has index 1. See `Statement::bind` 49 | /// for further details. 50 | pub fn bind(self, value: T) -> Result { 51 | #[allow(unused_mut)] 52 | let mut cursor = self.reset()?; 53 | cursor.statement.bind(value)?; 54 | Ok(cursor) 55 | } 56 | 57 | /// Bind values to parameters via an iterator. 58 | /// 59 | /// See `Statement::bind_iter` for further details. 60 | #[allow(unused_mut)] 61 | pub fn bind_iter(self, value: T) -> Result 62 | where 63 | T: IntoIterator, 64 | U: Bindable, 65 | { 66 | let mut cursor = self.reset()?; 67 | cursor.statement.bind_iter(value)?; 68 | Ok(cursor) 69 | } 70 | 71 | /// Reset the internal state. 72 | #[allow(unused_mut)] 73 | pub fn reset(mut self) -> Result { 74 | self.statement.reset()?; 75 | self.poisoned = false; 76 | Ok(self) 77 | } 78 | 79 | /// Advance to the next row and read all columns. 80 | pub fn try_next(&mut self) -> Result>> { 81 | if self.statement.next()? == State::Done { 82 | return Ok(None); 83 | } 84 | let mut values = Vec::with_capacity(self.column_count); 85 | for index in 0..self.column_count { 86 | values.push(self.statement.read(index)?); 87 | } 88 | Ok(Some(values)) 89 | } 90 | } 91 | 92 | impl<$($lifetime),+> Deref for $type<$($lifetime),+> { 93 | type Target = Statement<'l>; 94 | 95 | #[inline] 96 | fn deref(&self) -> &Self::Target { 97 | &self.statement 98 | } 99 | } 100 | 101 | impl<$($lifetime),+> Iterator for $type<$($lifetime),+> { 102 | type Item = Result; 103 | 104 | fn next(&mut self) -> Option { 105 | if self.poisoned { 106 | return None; 107 | } 108 | match self.try_next() { 109 | Ok(value) => { 110 | value.map(|values| Ok(Row { 111 | column_names: self.statement.column_names.clone(), 112 | column_mapping: self.statement.column_mapping(), 113 | values, 114 | })) 115 | } 116 | Err(error) => { 117 | self.poisoned = true; 118 | Some(Err(error)) 119 | } 120 | } 121 | } 122 | } 123 | } 124 | ); 125 | 126 | implement!(Cursor<'l, 'm>); 127 | implement!(CursorWithOwnership<'l>); 128 | 129 | impl<'l> From> for Statement<'l> { 130 | #[inline] 131 | fn from(cursor: CursorWithOwnership<'l>) -> Self { 132 | cursor.statement 133 | } 134 | } 135 | 136 | impl Row { 137 | /// Check if the row contains a column. 138 | /// 139 | /// In case of integer indices, the first column has index 0. 140 | #[inline] 141 | pub fn contains(&self, column: T) -> bool 142 | where 143 | T: RowIndex, 144 | { 145 | column.contains(self) 146 | } 147 | 148 | /// Read the value in a column. 149 | /// 150 | /// In case of integer indices, the first column has index 0. 151 | /// 152 | /// # Panics 153 | /// 154 | /// Panics if the column can not be read. 155 | #[inline] 156 | pub fn read<'l, T, U>(&'l self, column: U) -> T 157 | where 158 | T: TryFrom<&'l Value, Error = Error>, 159 | U: RowIndex, 160 | { 161 | self.try_read(column).unwrap() 162 | } 163 | 164 | /// Take the value from a column. 165 | /// 166 | /// In case of integer indices, the first column has index 0. Any subsequent invocation will 167 | /// result in `Value::Null`. 168 | #[inline] 169 | pub fn take(&mut self, column: U) -> Value 170 | where 171 | U: RowIndex, 172 | { 173 | let index = column.index(self); 174 | std::mem::take(&mut self.values[index]) 175 | } 176 | 177 | /// Try to read the value in a column. 178 | /// 179 | /// In case of integer indices, the first column has index 0. 180 | #[inline] 181 | pub fn try_read<'l, T, U>(&'l self, column: U) -> Result 182 | where 183 | T: TryFrom<&'l Value, Error = Error>, 184 | U: RowIndex, 185 | { 186 | if !column.contains(self) { 187 | raise!("the index is out of range ({column})"); 188 | } 189 | T::try_from(&self.values[column.index(self)]) 190 | } 191 | 192 | /// Iterate over the names and values. 193 | pub fn iter(&self) -> impl Iterator + use<'_> { 194 | self.column_names.iter().map(|column_name| { 195 | ( 196 | column_name.as_str(), 197 | &self.values[self.column_mapping[column_name]], 198 | ) 199 | }) 200 | } 201 | } 202 | 203 | impl From for Vec { 204 | #[inline] 205 | fn from(row: Row) -> Self { 206 | row.values 207 | } 208 | } 209 | 210 | impl Index for Row 211 | where 212 | T: RowIndex, 213 | { 214 | type Output = Value; 215 | 216 | fn index(&self, index: T) -> &Value { 217 | &self.values[index.index(self)] 218 | } 219 | } 220 | 221 | impl RowIndex for &str { 222 | #[inline] 223 | fn contains(&self, row: &Row) -> bool { 224 | row.column_mapping.contains_key(*self) 225 | } 226 | 227 | #[inline] 228 | fn index(self, row: &Row) -> usize { 229 | debug_assert!(RowIndex::contains(&self, row), "the index is out of range"); 230 | row.column_mapping[self] 231 | } 232 | } 233 | 234 | impl RowIndex for usize { 235 | #[inline] 236 | fn contains(&self, row: &Row) -> bool { 237 | self < &row.values.len() 238 | } 239 | 240 | #[inline] 241 | fn index(self, row: &Row) -> usize { 242 | debug_assert!(RowIndex::contains(&self, row), "the index is out of range"); 243 | self 244 | } 245 | } 246 | 247 | pub fn new<'l, 'm>(statement: &'m mut Statement<'l>) -> Cursor<'l, 'm> { 248 | Cursor { 249 | column_count: statement.column_count(), 250 | statement, 251 | poisoned: false, 252 | } 253 | } 254 | 255 | pub fn new_with_ownership(statement: Statement<'_>) -> CursorWithOwnership<'_> { 256 | CursorWithOwnership { 257 | column_count: statement.column_count(), 258 | statement, 259 | poisoned: false, 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /tests/statement.rs: -------------------------------------------------------------------------------- 1 | use sqlite::{Connection, State, Statement, Type, Value}; 2 | 3 | mod common; 4 | 5 | use common::{setup_english, setup_users}; 6 | 7 | macro_rules! ok(($result:expr) => ($result.unwrap())); 8 | 9 | #[test] 10 | fn bind_with_index() { 11 | let connection = setup_users(":memory:"); 12 | let query = "INSERT INTO users VALUES (?, ?, ?, ?, ?)"; 13 | let mut statement = ok!(connection.prepare(query)); 14 | 15 | ok!(statement.reset()); 16 | ok!(statement.bind(&[(1, 2i64)][..])); 17 | ok!(statement.bind((2, "Bob"))); 18 | ok!(statement.bind((3, 69.42))); 19 | ok!(statement.bind((4, &[0x69u8, 0x42u8][..]))); 20 | ok!(statement.bind((5, ()))); 21 | assert_eq!(ok!(statement.next()), State::Done); 22 | 23 | ok!(statement.reset()); 24 | ok!(statement.bind((1, Some(2i64)))); 25 | ok!(statement.bind((2, Some("Bob")))); 26 | ok!(statement.bind((3, Some(69.42)))); 27 | ok!(statement.bind((4, Some(&[0x69u8, 0x42u8][..])))); 28 | ok!(statement.bind((5, None::<&str>))); 29 | assert_eq!(ok!(statement.next()), State::Done); 30 | 31 | ok!(statement.reset()); 32 | ok!(statement.bind( 33 | &[ 34 | Value::Integer(2), 35 | Value::String("Bob".into()), 36 | Value::Float(69.42), 37 | Value::Binary([0x69u8, 0x42u8].to_vec()), 38 | Value::Null, 39 | ][..] 40 | )); 41 | assert_eq!(ok!(statement.next()), State::Done); 42 | 43 | ok!(statement.reset()); 44 | ok!(statement.bind( 45 | &[ 46 | Some(Value::Integer(2)), 47 | Some(Value::String("Bob".into())), 48 | Some(Value::Float(69.42)), 49 | Some(Value::Binary([0x69u8, 0x42u8].to_vec())), 50 | Some(Value::Null), 51 | ][..] 52 | )); 53 | assert_eq!(ok!(statement.next()), State::Done); 54 | 55 | ok!(statement.reset()); 56 | ok!(statement.bind( 57 | &[ 58 | (1, Value::Integer(2)), 59 | (2, Value::String("Bob".into())), 60 | (3, Value::Float(69.42)), 61 | (4, Value::Binary([0x69u8, 0x42u8].to_vec())), 62 | (5, Value::Null), 63 | ][..] 64 | )); 65 | assert_eq!(ok!(statement.next()), State::Done); 66 | } 67 | 68 | #[test] 69 | fn bind_with_name() { 70 | let connection = setup_users(":memory:"); 71 | let query = "INSERT INTO users VALUES (:id, :name, :age, :photo, :email)"; 72 | let mut statement = ok!(connection.prepare(query)); 73 | 74 | ok!(statement.reset()); 75 | ok!(statement.bind(&[(":id", 2i64)][..])); 76 | ok!(statement.bind((":name", "Bob"))); 77 | ok!(statement.bind((":age", 69.42))); 78 | ok!(statement.bind((":photo", &[0x69u8, 0x42u8][..]))); 79 | ok!(statement.bind((":email", ()))); 80 | assert_eq!(ok!(statement.next()), State::Done); 81 | 82 | ok!(statement.reset()); 83 | assert!(statement.bind((":missing", 404)).is_err()); 84 | 85 | ok!(statement.reset()); 86 | ok!(statement.bind( 87 | &[ 88 | (":id", Value::Integer(2)), 89 | (":name", Value::String("Bob".into())), 90 | (":age", Value::Float(69.42)), 91 | (":photo", Value::Binary([0x69u8, 0x42u8].to_vec())), 92 | (":email", Value::Null), 93 | ][..] 94 | )); 95 | assert_eq!(ok!(statement.next()), State::Done); 96 | } 97 | 98 | #[test] 99 | fn count() { 100 | let connection = setup_english(":memory:"); 101 | 102 | let query = "SELECT value FROM english WHERE value LIKE ?"; 103 | let mut statement = ok!(connection.prepare(query)); 104 | ok!(statement.bind((1, "%type"))); 105 | let mut count = 0; 106 | while let State::Row = ok!(statement.next()) { 107 | count += 1; 108 | } 109 | assert_eq!(count, 6); 110 | 111 | let query = "SELECT value FROM english WHERE value LIKE '%type'"; 112 | let mut statement = ok!(connection.prepare(query)); 113 | let mut count = 0; 114 | while let State::Row = ok!(statement.next()) { 115 | count += 1; 116 | } 117 | assert_eq!(count, 6); 118 | } 119 | 120 | #[test] 121 | fn read_with_index() { 122 | let connection = setup_users(":memory:"); 123 | let query = "SELECT * FROM users"; 124 | let mut statement = ok!(connection.prepare(query)); 125 | 126 | assert_eq!(ok!(statement.next()), State::Row); 127 | assert_eq!(ok!(statement.read::(0)), 1); 128 | assert_eq!(ok!(statement.read::(1)), String::from("Alice")); 129 | assert_eq!(ok!(statement.read::(2)), 42.69); 130 | assert_eq!(ok!(statement.read::, _>(3)), vec![0x42, 0x69]); 131 | assert_eq!(ok!(statement.read::(4)), Value::Null); 132 | assert_eq!(ok!(statement.next()), State::Done); 133 | } 134 | 135 | #[test] 136 | fn read_with_index_and_option() { 137 | let connection = setup_users(":memory:"); 138 | let query = "SELECT * FROM users"; 139 | let mut statement = ok!(connection.prepare(query)); 140 | 141 | assert_eq!(ok!(statement.next()), State::Row); 142 | assert_eq!(ok!(statement.read::, _>(0)), Some(1)); 143 | assert_eq!( 144 | ok!(statement.read::, _>(1)), 145 | Some(String::from("Alice")) 146 | ); 147 | assert_eq!(ok!(statement.read::, _>(2)), Some(42.69)); 148 | assert_eq!( 149 | ok!(statement.read::>, _>(3)), 150 | Some(vec![0x42, 0x69]) 151 | ); 152 | assert_eq!(ok!(statement.read::, _>(4)), None); 153 | assert_eq!(ok!(statement.next()), State::Done); 154 | } 155 | 156 | #[test] 157 | fn read_with_name_and_option() { 158 | let connection = setup_users(":memory:"); 159 | let query = "SELECT * FROM users"; 160 | let mut statement = ok!(connection.prepare(query)); 161 | 162 | assert_eq!(ok!(statement.next()), State::Row); 163 | assert_eq!(ok!(statement.read::, _>("id")), Some(1)); 164 | assert_eq!( 165 | ok!(statement.read::, _>("name")), 166 | Some(String::from("Alice")) 167 | ); 168 | assert_eq!(ok!(statement.read::, _>("age")), Some(42.69)); 169 | assert_eq!( 170 | ok!(statement.read::>, _>("photo")), 171 | Some(vec![0x42, 0x69]) 172 | ); 173 | assert_eq!(ok!(statement.read::, _>("email")), None); 174 | assert_eq!(ok!(statement.next()), State::Done); 175 | } 176 | 177 | #[test] 178 | fn read_with_name() { 179 | let connection = setup_users(":memory:"); 180 | let query = "SELECT * FROM users"; 181 | let mut statement = ok!(connection.prepare(query)); 182 | 183 | assert_eq!(ok!(statement.next()), State::Row); 184 | assert_eq!(ok!(statement.read::("id")), 1); 185 | assert_eq!( 186 | ok!(statement.read::("name")), 187 | String::from("Alice") 188 | ); 189 | assert_eq!(ok!(statement.read::("age")), 42.69); 190 | assert_eq!(ok!(statement.read::, _>("photo")), vec![0x42, 0x69]); 191 | assert_eq!(ok!(statement.read::("email")), Value::Null); 192 | assert_eq!(ok!(statement.next()), State::Done); 193 | } 194 | 195 | #[test] 196 | fn column_count() { 197 | let connection = setup_users(":memory:"); 198 | let query = "SELECT * FROM users"; 199 | let mut statement = ok!(connection.prepare(query)); 200 | 201 | assert_eq!(ok!(statement.next()), State::Row); 202 | assert_eq!(statement.column_count(), 5); 203 | } 204 | 205 | #[test] 206 | fn column_name() { 207 | let connection = setup_users(":memory:"); 208 | let query = "SELECT id, name, age, photo AS user_photo FROM users"; 209 | let statement = ok!(connection.prepare(query)); 210 | 211 | let names = statement.column_names(); 212 | assert_eq!(names, vec!["id", "name", "age", "user_photo"]); 213 | assert_eq!("user_photo", ok!(statement.column_name(3))); 214 | } 215 | 216 | #[test] 217 | fn column_type() { 218 | let connection = setup_users(":memory:"); 219 | let query = "SELECT * FROM users"; 220 | let mut statement = ok!(connection.prepare(query)); 221 | 222 | assert_eq!(ok!(statement.column_type(0)), Type::Null); 223 | assert_eq!(ok!(statement.column_type(1)), Type::Null); 224 | assert_eq!(ok!(statement.column_type(2)), Type::Null); 225 | assert_eq!(ok!(statement.column_type(3)), Type::Null); 226 | 227 | assert_eq!(ok!(statement.next()), State::Row); 228 | 229 | assert_eq!(ok!(statement.column_type(0)), Type::Integer); 230 | assert_eq!(ok!(statement.column_type(1)), Type::String); 231 | assert_eq!(ok!(statement.column_type(2)), Type::Float); 232 | assert_eq!(ok!(statement.column_type(3)), Type::Binary); 233 | } 234 | 235 | #[test] 236 | fn parameter_index() { 237 | let connection = setup_users(":memory:"); 238 | let query = "INSERT INTO users VALUES (:id, :name, :age, :photo, :email)"; 239 | let mut statement = ok!(connection.prepare(query)); 240 | ok!(statement.bind((":id", 2i64))); 241 | ok!(statement.bind((":name", "Bob"))); 242 | ok!(statement.bind((":age", 69.42))); 243 | ok!(statement.bind((":photo", &[0x69u8, 0x42u8][..]))); 244 | ok!(statement.bind((":email", ()))); 245 | assert_eq!(ok!(statement.parameter_index(":missing")), None); 246 | assert_eq!(ok!(statement.next()), State::Done); 247 | } 248 | 249 | #[test] 250 | fn workflow_1() { 251 | struct Database<'l> { 252 | #[allow(dead_code)] 253 | connection: &'l Connection, 254 | statement: Statement<'l>, 255 | } 256 | 257 | impl Database<'_> { 258 | fn run_once(&mut self) -> sqlite::Result<()> { 259 | self.statement.reset()?; 260 | self.statement.bind((":age", 40))?; 261 | assert_eq!(ok!(self.statement.next()), State::Row); 262 | Ok(()) 263 | } 264 | } 265 | 266 | let connection = setup_users(":memory:"); 267 | let query = "SELECT name FROM users WHERE age > :age"; 268 | let statement = ok!(connection.prepare(query)); 269 | 270 | let mut database = Database { 271 | connection: &connection, 272 | statement, 273 | }; 274 | 275 | for _ in 0..5 { 276 | assert!(database.run_once().is_ok()); 277 | } 278 | } 279 | 280 | #[test] 281 | fn workflow_2() { 282 | let connection = ok!(Connection::open(":memory:")); 283 | ok!(connection.execute("CREATE TABLE users (name TEXT, age INTEGER, PRIMARY KEY (name))")); 284 | 285 | let mut statement = ok!(connection.prepare( 286 | "INSERT INTO users (name, age) VALUES ('jean', 49) ON CONFLICT DO UPDATE SET age = 49" 287 | )); 288 | ok!(statement.next()); 289 | 290 | let mut statement = ok!(connection.prepare( 291 | "INSERT INTO users (name, age) VALUES ('jean', 50) ON CONFLICT DO UPDATE SET age = 50" 292 | )); 293 | ok!(statement.next()); 294 | 295 | let mut statement = ok!(connection.prepare("SELECT * FROM users WHERE name = 'jean'")); 296 | ok!(statement.next()); 297 | 298 | let age = ok!(statement.read::("age")); 299 | assert_eq!(age, 50); 300 | } 301 | -------------------------------------------------------------------------------- /src/connection.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::{c_char, c_int, c_void}; 2 | use std::marker::PhantomData; 3 | use std::ops::{Deref, DerefMut}; 4 | use std::path::Path; 5 | 6 | use crate::error::Result; 7 | use crate::statement::Statement; 8 | 9 | /// A connection. 10 | pub struct Connection { 11 | raw: Raw, 12 | busy_callback: Option bool + Send>>, 13 | phantom: PhantomData, 14 | } 15 | 16 | /// A thread-safe connection. 17 | pub struct ConnectionThreadSafe(Connection); 18 | 19 | /// Flags for opening a connection. 20 | #[derive(Clone, Copy, Debug)] 21 | pub struct OpenFlags(c_int); 22 | 23 | struct Raw(*mut ffi::sqlite3); 24 | 25 | impl Connection { 26 | /// Open a read-write connection to a new or existing database. 27 | pub fn open>(path: T) -> Result { 28 | Connection::open_with_flags(path, OpenFlags::new().with_create().with_read_write()) 29 | } 30 | 31 | /// Open a connection with specific flags. 32 | pub fn open_with_flags>(path: T, flags: OpenFlags) -> Result { 33 | let mut raw = std::ptr::null_mut(); 34 | unsafe { 35 | let code = ffi::sqlite3_open_v2( 36 | path_to_cstr!(path.as_ref()).as_ptr(), 37 | &mut raw, 38 | flags.0, 39 | std::ptr::null(), 40 | ); 41 | match code { 42 | ffi::SQLITE_OK => {} 43 | code => match crate::error::last(raw) { 44 | Some(error) => { 45 | ffi::sqlite3_close(raw); 46 | return Err(error); 47 | } 48 | _ => { 49 | ffi::sqlite3_close(raw); 50 | return Err(crate::error::Error { 51 | code: Some(code as isize), 52 | message: None, 53 | }); 54 | } 55 | }, 56 | } 57 | } 58 | Ok(Connection { 59 | raw: Raw(raw), 60 | busy_callback: None, 61 | phantom: PhantomData, 62 | }) 63 | } 64 | 65 | /// Open a thread-safe read-write connection to a new or existing database. 66 | pub fn open_thread_safe>(path: T) -> Result { 67 | Connection::open_with_flags( 68 | path, 69 | OpenFlags::new() 70 | .with_create() 71 | .with_read_write() 72 | .with_full_mutex(), 73 | ) 74 | .map(ConnectionThreadSafe) 75 | } 76 | 77 | /// Open a thread-safe connection with specific flags. 78 | pub fn open_thread_safe_with_flags>( 79 | path: T, 80 | flags: OpenFlags, 81 | ) -> Result { 82 | Connection::open_with_flags(path, flags.with_full_mutex()).map(ConnectionThreadSafe) 83 | } 84 | 85 | #[doc(hidden)] 86 | #[inline] 87 | pub fn as_raw(&self) -> *mut ffi::sqlite3 { 88 | self.raw.0 89 | } 90 | } 91 | 92 | impl Connection { 93 | /// Execute a statement without processing the resulting rows if any. 94 | #[inline] 95 | pub fn execute>(&self, statement: T) -> Result<()> { 96 | unsafe { 97 | ok!( 98 | self.raw.0, 99 | ffi::sqlite3_exec( 100 | self.raw.0, 101 | str_to_cstr!(statement.as_ref()).as_ptr(), 102 | None, 103 | std::ptr::null_mut(), 104 | std::ptr::null_mut(), 105 | ) 106 | ); 107 | } 108 | Ok(()) 109 | } 110 | 111 | /// Execute a statement and process the resulting rows as plain text. 112 | /// 113 | /// The callback is triggered for each row. If the callback returns `false`, no more rows will 114 | /// be processed. For large queries and non-string data types, prepared statement are highly 115 | /// preferable; see `prepare`. 116 | #[inline] 117 | pub fn iterate, F>(&self, statement: T, callback: F) -> Result<()> 118 | where 119 | F: FnMut(&[(&str, Option<&str>)]) -> bool, 120 | { 121 | unsafe { 122 | let callback = Box::new(callback); 123 | ok!( 124 | self.raw.0, 125 | ffi::sqlite3_exec( 126 | self.raw.0, 127 | str_to_cstr!(statement.as_ref()).as_ptr(), 128 | Some(process_callback::), 129 | &*callback as *const F as *mut F as *mut _, 130 | std::ptr::null_mut(), 131 | ) 132 | ); 133 | } 134 | Ok(()) 135 | } 136 | 137 | /// Create a prepared statement. 138 | #[inline] 139 | pub fn prepare>(&self, statement: T) -> Result> { 140 | crate::statement::new(self.raw.0, statement) 141 | } 142 | 143 | /// Return the number of rows inserted, updated, or deleted by the most recent INSERT, UPDATE, 144 | /// or DELETE statement. 145 | #[inline] 146 | pub fn change_count(&self) -> usize { 147 | unsafe { ffi::sqlite3_changes(self.raw.0) as usize } 148 | } 149 | 150 | /// Return the total number of rows inserted, updated, and deleted by all INSERT, UPDATE, and 151 | /// DELETE statements since the connection was opened. 152 | #[inline] 153 | pub fn total_change_count(&self) -> usize { 154 | unsafe { ffi::sqlite3_total_changes(self.raw.0) as usize } 155 | } 156 | } 157 | 158 | impl Connection { 159 | /// Set a callback for handling busy events. 160 | /// 161 | /// The callback is triggered when the database cannot perform an operation due to processing 162 | /// of some other request. If the callback returns `true`, the operation will be repeated. 163 | pub fn set_busy_handler(&mut self, callback: F) -> Result<()> 164 | where 165 | F: FnMut(usize) -> bool + Send + 'static, 166 | { 167 | self.remove_busy_handler()?; 168 | unsafe { 169 | let callback = Box::new(callback); 170 | let result = ffi::sqlite3_busy_handler( 171 | self.raw.0, 172 | Some(busy_callback::), 173 | &*callback as *const F as *mut F as *mut _, 174 | ); 175 | self.busy_callback = Some(callback); 176 | ok!(self.raw.0, result); 177 | } 178 | Ok(()) 179 | } 180 | 181 | /// Set an implicit callback for handling busy events that tries to repeat rejected operations 182 | /// until a timeout expires. 183 | #[inline] 184 | pub fn set_busy_timeout(&mut self, milliseconds: usize) -> Result<()> { 185 | unsafe { 186 | ok!( 187 | self.raw.0, 188 | ffi::sqlite3_busy_timeout(self.raw.0, milliseconds as c_int) 189 | ); 190 | } 191 | Ok(()) 192 | } 193 | 194 | /// Remove the callback handling busy events. 195 | #[inline] 196 | pub fn remove_busy_handler(&mut self) -> Result<()> { 197 | self.busy_callback = None; 198 | unsafe { 199 | ok!( 200 | self.raw.0, 201 | ffi::sqlite3_busy_handler(self.raw.0, None, std::ptr::null_mut()) 202 | ); 203 | } 204 | Ok(()) 205 | } 206 | } 207 | 208 | impl Connection { 209 | /// Enable loading extensions. 210 | #[cfg(feature = "extension")] 211 | #[inline] 212 | pub fn enable_extension(&self) -> Result<()> { 213 | unsafe { 214 | ok!( 215 | self.raw.0, 216 | ffi::sqlite3_enable_load_extension(self.raw.0, 1 as c_int) 217 | ); 218 | } 219 | Ok(()) 220 | } 221 | 222 | /// Disable loading extensions. 223 | #[cfg(feature = "extension")] 224 | #[inline] 225 | pub fn disable_extension(&self) -> Result<()> { 226 | unsafe { 227 | ok!( 228 | self.raw.0, 229 | ffi::sqlite3_enable_load_extension(self.raw.0, 0 as c_int) 230 | ); 231 | } 232 | Ok(()) 233 | } 234 | 235 | /// Load an extension. 236 | #[cfg(feature = "extension")] 237 | #[inline] 238 | pub fn load_extension>(&self, name: T) -> Result<()> { 239 | unsafe { 240 | ok!( 241 | self.raw.0, 242 | ffi::sqlite3_load_extension( 243 | self.raw.0, 244 | str_to_cstr!(name.as_ref()).as_ptr() as *const c_char, 245 | std::ptr::null_mut(), 246 | std::ptr::null_mut(), 247 | ) 248 | ); 249 | } 250 | Ok(()) 251 | } 252 | } 253 | 254 | impl Connection { 255 | /// Set the encryption key. 256 | #[cfg(feature = "encryption")] 257 | #[inline] 258 | pub fn set_encryption_key>(&self, key: T) -> Result<()> { 259 | unsafe { 260 | ok!( 261 | self.raw.0, 262 | ffi::sqlite3_key_v2( 263 | self.raw.0, 264 | std::ptr::null() as *const c_char, 265 | str_to_cstr!(key.as_ref()).as_ptr() as *const c_void, 266 | key.as_ref().len() as c_int, 267 | ) 268 | ); 269 | } 270 | Ok(()) 271 | } 272 | 273 | /// Change the encryption key. 274 | #[cfg(feature = "encryption")] 275 | #[inline] 276 | pub fn change_encryption_key>(&self, new_key: T) -> Result<()> { 277 | unsafe { 278 | ok!( 279 | self.raw.0, 280 | ffi::sqlite3_rekey_v2( 281 | self.raw.0, 282 | std::ptr::null() as *const c_char, 283 | str_to_cstr!(new_key.as_ref()).as_ptr() as *const c_void, 284 | new_key.as_ref().len() as c_int, 285 | ) 286 | ); 287 | } 288 | Ok(()) 289 | } 290 | } 291 | 292 | impl Drop for Connection { 293 | #[inline] 294 | #[allow(unused_must_use)] 295 | fn drop(&mut self) { 296 | self.remove_busy_handler(); 297 | unsafe { ffi::sqlite3_close(self.raw.0) }; 298 | } 299 | } 300 | 301 | impl OpenFlags { 302 | /// Create flags for opening a database connection. 303 | #[inline] 304 | pub fn new() -> Self { 305 | OpenFlags(0) 306 | } 307 | 308 | /// Create the database if it does not already exist. 309 | pub fn with_create(mut self) -> Self { 310 | self.0 |= ffi::SQLITE_OPEN_CREATE; 311 | self 312 | } 313 | 314 | /// Open the database in the serialized [threading mode][1]. 315 | /// 316 | /// [1]: https://www.sqlite.org/threadsafe.html 317 | pub fn with_full_mutex(mut self) -> Self { 318 | self.0 |= ffi::SQLITE_OPEN_FULLMUTEX; 319 | self 320 | } 321 | 322 | /// Opens the database in the multi-thread [threading mode][1]. 323 | /// 324 | /// [1]: https://www.sqlite.org/threadsafe.html 325 | pub fn with_no_mutex(mut self) -> Self { 326 | self.0 |= ffi::SQLITE_OPEN_NOMUTEX; 327 | self 328 | } 329 | 330 | /// Open the database for reading only. 331 | pub fn with_read_only(mut self) -> Self { 332 | self.0 |= ffi::SQLITE_OPEN_READONLY; 333 | self 334 | } 335 | 336 | /// Open the database for reading and writing. 337 | pub fn with_read_write(mut self) -> Self { 338 | self.0 |= ffi::SQLITE_OPEN_READWRITE; 339 | self 340 | } 341 | 342 | /// Allow the path to be interpreted as a URI. 343 | pub fn with_uri(mut self) -> Self { 344 | self.0 |= ffi::SQLITE_OPEN_URI; 345 | self 346 | } 347 | } 348 | 349 | impl Default for OpenFlags { 350 | #[inline] 351 | fn default() -> Self { 352 | Self::new() 353 | } 354 | } 355 | 356 | impl Deref for ConnectionThreadSafe { 357 | type Target = Connection; 358 | 359 | #[inline] 360 | fn deref(&self) -> &Self::Target { 361 | &self.0 362 | } 363 | } 364 | 365 | impl DerefMut for ConnectionThreadSafe { 366 | #[inline] 367 | fn deref_mut(&mut self) -> &mut Self::Target { 368 | &mut self.0 369 | } 370 | } 371 | 372 | unsafe impl Sync for ConnectionThreadSafe {} 373 | 374 | unsafe impl Send for Raw {} 375 | 376 | extern "C" fn busy_callback(callback: *mut c_void, attempts: c_int) -> c_int 377 | where 378 | F: FnMut(usize) -> bool, 379 | { 380 | unsafe { c_int::from((*(callback as *mut F))(attempts as usize)) } 381 | } 382 | 383 | extern "C" fn process_callback( 384 | callback: *mut c_void, 385 | count: c_int, 386 | values: *mut *mut c_char, 387 | columns: *mut *mut c_char, 388 | ) -> c_int 389 | where 390 | F: FnMut(&[(&str, Option<&str>)]) -> bool, 391 | { 392 | unsafe { 393 | let mut pairs = Vec::with_capacity(count as usize); 394 | for index in 0..(count as isize) { 395 | let column = { 396 | let pointer = *columns.offset(index); 397 | debug_assert!(!pointer.is_null()); 398 | c_str_to_str!(pointer).unwrap() 399 | }; 400 | let value = { 401 | let pointer = *values.offset(index); 402 | if pointer.is_null() { 403 | None 404 | } else { 405 | Some(c_str_to_str!(pointer).unwrap()) 406 | } 407 | }; 408 | pairs.push((column, value)); 409 | } 410 | c_int::from(!(*(callback as *mut F))(&pairs)) 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/statement.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::{c_double, c_int}; 2 | use std::collections::HashMap; 3 | use std::marker::PhantomData; 4 | use std::rc::Rc; 5 | 6 | use crate::cursor::{Cursor, CursorWithOwnership, Row}; 7 | use crate::error::Result; 8 | use crate::value::{Type, Value}; 9 | 10 | // https://sqlite.org/c3ref/c_static.html 11 | macro_rules! transient( 12 | () => ( 13 | std::mem::transmute::< 14 | *const std::ffi::c_void, 15 | std::option::Option 16 | >(!0 as *const core::ffi::c_void) 17 | ); 18 | ); 19 | 20 | /// A prepared statement. 21 | pub struct Statement<'l> { 22 | raw: (*mut ffi::sqlite3_stmt, *mut ffi::sqlite3), 23 | pub(crate) column_names: Rc>, 24 | column_mapping: Rc>, 25 | phantom: PhantomData<(ffi::sqlite3_stmt, &'l ffi::sqlite3)>, 26 | } 27 | 28 | /// A type suitable for binding to a prepared statement. 29 | pub trait Bindable { 30 | /// Bind to a parameter. 31 | fn bind(self, _: &mut Statement) -> Result<()>; 32 | } 33 | 34 | /// A type suitable for binding to a prepared statement given a parameter index. 35 | pub trait BindableWithIndex { 36 | /// Bind to a parameter. 37 | /// 38 | /// In case of integer indices, the first parameter has index 1. 39 | fn bind(self, _: &mut Statement, _: T) -> Result<()>; 40 | } 41 | 42 | /// A type suitable for indexing columns in a prepared statement. 43 | pub trait ColumnIndex: Copy + std::fmt::Debug { 44 | /// Identify the ordinal position. 45 | /// 46 | /// The first column has index 0. 47 | fn index(self, statement: &Statement) -> Result; 48 | } 49 | 50 | /// A type suitable for indexing parameters in a prepared statement. 51 | pub trait ParameterIndex: Copy + std::fmt::Debug { 52 | /// Identify the ordinal position. 53 | /// 54 | /// The first parameter has index 1. 55 | fn index(self, statement: &Statement) -> Result; 56 | } 57 | 58 | /// A type suitable for reading from a prepared statement given a column index. 59 | pub trait ReadableWithIndex: Sized { 60 | /// Read from a column. 61 | /// 62 | /// In case of integer indices, the first column has index 0. 63 | fn read(_: &Statement, _: T) -> Result; 64 | } 65 | 66 | /// The state of a prepared statement. 67 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 68 | pub enum State { 69 | /// There is a row available for reading. 70 | Row, 71 | /// The statement has been entirely evaluated. 72 | Done, 73 | } 74 | 75 | impl<'l> Statement<'l> { 76 | /// Bind values to parameters. 77 | /// 78 | /// In case of integer indices, the first parameter has index 1. 79 | /// 80 | /// # Examples 81 | /// 82 | /// ``` 83 | /// # let connection = sqlite::open(":memory:").unwrap(); 84 | /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)"); 85 | /// let query = "SELECT * FROM users WHERE name = ?"; 86 | /// let mut statement = connection.prepare(query)?; 87 | /// statement.bind((1, "Bob"))?; 88 | /// # Ok::<(), sqlite::Error>(()) 89 | /// ``` 90 | /// 91 | /// ``` 92 | /// # let connection = sqlite::open(":memory:").unwrap(); 93 | /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)"); 94 | /// let query = "SELECT * FROM users WHERE name = ?"; 95 | /// let mut statement = connection.prepare(query)?; 96 | /// statement.bind(&[(1, "Bob")][..])?; 97 | /// # Ok::<(), sqlite::Error>(()) 98 | /// ``` 99 | /// 100 | /// ``` 101 | /// # let connection = sqlite::open(":memory:").unwrap(); 102 | /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)"); 103 | /// let query = "SELECT * FROM users WHERE name = :name"; 104 | /// let mut statement = connection.prepare(query)?; 105 | /// statement.bind((":name", "Bob"))?; 106 | /// # Ok::<(), sqlite::Error>(()) 107 | /// ``` 108 | /// 109 | /// ``` 110 | /// # let connection = sqlite::open(":memory:").unwrap(); 111 | /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)"); 112 | /// let query = "SELECT * FROM users WHERE name = :name"; 113 | /// let mut statement = connection.prepare(query)?; 114 | /// statement.bind(&[(":name", "Bob")][..])?; 115 | /// # Ok::<(), sqlite::Error>(()) 116 | /// ``` 117 | /// 118 | /// ``` 119 | /// # use sqlite::Value; 120 | /// # let connection = sqlite::open(":memory:").unwrap(); 121 | /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)"); 122 | /// let query = "SELECT * FROM users WHERE id = :id AND name = :name"; 123 | /// let mut statement = connection.prepare(query)?; 124 | /// statement.bind::<&[(_, Value)]>(&[ 125 | /// (":id", 1.into()), 126 | /// (":name", "Bob".into()), 127 | /// ][..])?; 128 | /// # Ok::<(), sqlite::Error>(()) 129 | /// ``` 130 | #[inline] 131 | pub fn bind(&mut self, value: T) -> Result<()> { 132 | value.bind(self)?; 133 | Ok(()) 134 | } 135 | 136 | /// Bind values to parameters via an iterator. 137 | /// 138 | /// # Examples 139 | /// 140 | /// ``` 141 | /// # use sqlite::Value; 142 | /// # let connection = sqlite::open(":memory:").unwrap(); 143 | /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)"); 144 | /// let query = "INSERT INTO users VALUES (:id, :name)"; 145 | /// let mut statement = connection.prepare(query)?; 146 | /// statement.bind_iter::<_, (_, Value)>([ 147 | /// (":name", "Bob".into()), 148 | /// (":id", 42.into()), 149 | /// ])?; 150 | /// # Ok::<(), sqlite::Error>(()) 151 | /// ``` 152 | pub fn bind_iter(&mut self, value: T) -> Result<()> 153 | where 154 | T: IntoIterator, 155 | U: Bindable, 156 | { 157 | for value in value { 158 | self.bind(value)?; 159 | } 160 | Ok(()) 161 | } 162 | 163 | /// Create a cursor. 164 | #[inline] 165 | pub fn iter(&mut self) -> Cursor<'l, '_> { 166 | self.into() 167 | } 168 | 169 | /// Advance to the next state. 170 | /// 171 | /// The function should be called multiple times until `State::Done` is reached in order to 172 | /// evaluate the statement entirely. 173 | #[allow(clippy::should_implement_trait)] 174 | pub fn next(&mut self) -> Result { 175 | Ok(match unsafe { ffi::sqlite3_step(self.raw.0) } { 176 | ffi::SQLITE_ROW => State::Row, 177 | ffi::SQLITE_DONE => State::Done, 178 | code => error!(self.raw.1, code), 179 | }) 180 | } 181 | 182 | /// Read a value from a column. 183 | /// 184 | /// In case of integer indices, the first column has index 0. 185 | #[inline] 186 | pub fn read(&self, index: U) -> Result 187 | where 188 | T: ReadableWithIndex, 189 | U: ColumnIndex, 190 | { 191 | ReadableWithIndex::read(self, index) 192 | } 193 | 194 | /// Return the number of columns. 195 | #[inline] 196 | pub fn column_count(&self) -> usize { 197 | self.column_names.len() 198 | } 199 | 200 | #[doc(hidden)] 201 | #[inline] 202 | pub fn column_mapping(&self) -> Rc> { 203 | self.column_mapping.clone() 204 | } 205 | 206 | /// Return the name of a column. 207 | /// 208 | /// In case of integer indices, the first column has index 0. 209 | #[inline] 210 | pub fn column_name(&self, index: T) -> Result<&str> { 211 | Ok(&self.column_names[index.index(self)?]) 212 | } 213 | 214 | /// Return column names. 215 | #[inline] 216 | pub fn column_names(&self) -> &[String] { 217 | &self.column_names 218 | } 219 | 220 | /// Return the type of a column. 221 | /// 222 | /// The type becomes available after taking a step. In case of integer indices, the first 223 | /// column has index 0. 224 | pub fn column_type(&self, index: T) -> Result { 225 | Ok( 226 | match unsafe { ffi::sqlite3_column_type(self.raw.0, index.index(self)? as c_int) } { 227 | ffi::SQLITE_BLOB => Type::Binary, 228 | ffi::SQLITE_FLOAT => Type::Float, 229 | ffi::SQLITE_INTEGER => Type::Integer, 230 | ffi::SQLITE_TEXT => Type::String, 231 | ffi::SQLITE_NULL => Type::Null, 232 | _ => unreachable!(), 233 | }, 234 | ) 235 | } 236 | 237 | /// Return the index for a named parameter if exists. 238 | /// 239 | /// # Examples 240 | /// 241 | /// ``` 242 | /// # let connection = sqlite::open(":memory:").unwrap(); 243 | /// # connection.execute("CREATE TABLE users (name STRING)"); 244 | /// let query = "SELECT * FROM users WHERE name = :name"; 245 | /// let statement = connection.prepare(query)?; 246 | /// assert_eq!(statement.parameter_index(":name")?.unwrap(), 1); 247 | /// assert_eq!(statement.parameter_index(":asdf")?, None); 248 | /// # Ok::<(), sqlite::Error>(()) 249 | /// ``` 250 | pub fn parameter_index(&self, parameter: &str) -> Result> { 251 | let index = unsafe { 252 | ffi::sqlite3_bind_parameter_index(self.raw.0, str_to_cstr!(parameter).as_ptr()) 253 | }; 254 | match index { 255 | 0 => Ok(None), 256 | _ => Ok(Some(index as usize)), 257 | } 258 | } 259 | 260 | /// Reset the internal state. 261 | #[inline] 262 | pub fn reset(&mut self) -> Result<()> { 263 | unsafe { ok!(self.raw.1, ffi::sqlite3_reset(self.raw.0)) }; 264 | Ok(()) 265 | } 266 | 267 | #[doc(hidden)] 268 | #[inline] 269 | pub fn as_raw(&self) -> *mut ffi::sqlite3_stmt { 270 | self.raw.0 271 | } 272 | } 273 | 274 | impl Drop for Statement<'_> { 275 | #[inline] 276 | fn drop(&mut self) { 277 | unsafe { ffi::sqlite3_finalize(self.raw.0) }; 278 | } 279 | } 280 | 281 | impl<'l, 'm> From<&'m mut Statement<'l>> for Cursor<'l, 'm> { 282 | #[inline] 283 | fn from(statement: &'m mut Statement<'l>) -> Self { 284 | crate::cursor::new(statement) 285 | } 286 | } 287 | 288 | impl<'l> IntoIterator for Statement<'l> { 289 | type Item = Result; 290 | type IntoIter = CursorWithOwnership<'l>; 291 | 292 | #[inline] 293 | fn into_iter(self) -> Self::IntoIter { 294 | crate::cursor::new_with_ownership(self) 295 | } 296 | } 297 | 298 | impl Bindable for (T, U) 299 | where 300 | T: ParameterIndex, 301 | U: BindableWithIndex, 302 | { 303 | #[inline] 304 | fn bind(self, statement: &mut Statement) -> Result<()> { 305 | self.1.bind(statement, self.0) 306 | } 307 | } 308 | 309 | impl Bindable for &[T] 310 | where 311 | T: BindableWithIndex + Clone, 312 | { 313 | fn bind(self, statement: &mut Statement) -> Result<()> { 314 | for (index, value) in self.iter().enumerate() { 315 | value.clone().bind(statement, index + 1)?; 316 | } 317 | Ok(()) 318 | } 319 | } 320 | 321 | impl Bindable for &[(T, U)] 322 | where 323 | T: ParameterIndex, 324 | U: BindableWithIndex + Clone, 325 | { 326 | fn bind(self, statement: &mut Statement) -> Result<()> { 327 | for (index, value) in self.iter() { 328 | value.clone().bind(statement, *index)?; 329 | } 330 | Ok(()) 331 | } 332 | } 333 | 334 | impl BindableWithIndex for &[u8] { 335 | fn bind(self, statement: &mut Statement, index: T) -> Result<()> { 336 | unsafe { 337 | ok!( 338 | statement.raw.1, 339 | ffi::sqlite3_bind_blob( 340 | statement.raw.0, 341 | index.index(statement)? as c_int, 342 | self.as_ptr() as *const _, 343 | self.len() as c_int, 344 | transient!(), 345 | ) 346 | ); 347 | } 348 | Ok(()) 349 | } 350 | } 351 | 352 | impl BindableWithIndex for f64 { 353 | fn bind(self, statement: &mut Statement, index: T) -> Result<()> { 354 | unsafe { 355 | ok!( 356 | statement.raw.1, 357 | ffi::sqlite3_bind_double( 358 | statement.raw.0, 359 | index.index(statement)? as c_int, 360 | self as c_double 361 | ) 362 | ); 363 | } 364 | Ok(()) 365 | } 366 | } 367 | 368 | impl BindableWithIndex for i64 { 369 | fn bind(self, statement: &mut Statement, index: T) -> Result<()> { 370 | unsafe { 371 | ok!( 372 | statement.raw.1, 373 | ffi::sqlite3_bind_int64( 374 | statement.raw.0, 375 | index.index(statement)? as c_int, 376 | self as ffi::sqlite3_int64 377 | ) 378 | ); 379 | } 380 | Ok(()) 381 | } 382 | } 383 | 384 | impl BindableWithIndex for &str { 385 | fn bind(self, statement: &mut Statement, index: T) -> Result<()> { 386 | unsafe { 387 | ok!( 388 | statement.raw.1, 389 | ffi::sqlite3_bind_text( 390 | statement.raw.0, 391 | index.index(statement)? as c_int, 392 | self.as_ptr() as *const _, 393 | self.len() as c_int, 394 | transient!(), 395 | ) 396 | ); 397 | } 398 | Ok(()) 399 | } 400 | } 401 | 402 | impl BindableWithIndex for () { 403 | fn bind(self, statement: &mut Statement, index: T) -> Result<()> { 404 | unsafe { 405 | ok!( 406 | statement.raw.1, 407 | ffi::sqlite3_bind_null(statement.raw.0, index.index(statement)? as c_int) 408 | ); 409 | } 410 | Ok(()) 411 | } 412 | } 413 | 414 | impl BindableWithIndex for Value { 415 | #[inline] 416 | fn bind(self, statement: &mut Statement, index: T) -> Result<()> { 417 | (index, &self).bind(statement) 418 | } 419 | } 420 | 421 | impl BindableWithIndex for &Value { 422 | fn bind(self, statement: &mut Statement, index: T) -> Result<()> { 423 | match self { 424 | Value::Binary(ref value) => (value as &[u8]).bind(statement, index), 425 | Value::Float(value) => value.bind(statement, index), 426 | Value::Integer(value) => value.bind(statement, index), 427 | Value::String(ref value) => (value as &str).bind(statement, index), 428 | Value::Null => ().bind(statement, index), 429 | } 430 | } 431 | } 432 | 433 | impl BindableWithIndex for Option 434 | where 435 | T: BindableWithIndex, 436 | { 437 | #[inline] 438 | fn bind(self, statement: &mut Statement, index: U) -> Result<()> { 439 | match self { 440 | Some(value) => value.bind(statement, index), 441 | None => ().bind(statement, index), 442 | } 443 | } 444 | } 445 | 446 | impl BindableWithIndex for &Option 447 | where 448 | T: BindableWithIndex + Clone, 449 | { 450 | #[inline] 451 | fn bind(self, statement: &mut Statement, index: U) -> Result<()> { 452 | match self { 453 | Some(value) => value.clone().bind(statement, index), 454 | None => ().bind(statement, index), 455 | } 456 | } 457 | } 458 | 459 | impl ColumnIndex for &str { 460 | #[inline] 461 | fn index(self, statement: &Statement) -> Result { 462 | if statement.column_mapping.contains_key(self) { 463 | Ok(statement.column_mapping[self]) 464 | } else { 465 | raise!("the index is out of range ({})", self); 466 | } 467 | } 468 | } 469 | 470 | impl ColumnIndex for usize { 471 | #[inline] 472 | fn index(self, statement: &Statement) -> Result { 473 | if self < statement.column_count() { 474 | Ok(self) 475 | } else { 476 | raise!("the index is out of range ({})", self); 477 | } 478 | } 479 | } 480 | 481 | impl ParameterIndex for &str { 482 | #[inline] 483 | fn index(self, statement: &Statement) -> Result { 484 | match statement.parameter_index(self)? { 485 | Some(index) => Ok(index), 486 | _ => raise!("the index is out of range ({})", self), 487 | } 488 | } 489 | } 490 | 491 | impl ParameterIndex for usize { 492 | #[inline] 493 | fn index(self, _: &Statement) -> Result { 494 | if self > 0 { 495 | Ok(self) 496 | } else { 497 | raise!("the index is out of range ({})", self); 498 | } 499 | } 500 | } 501 | 502 | impl ReadableWithIndex for Vec { 503 | fn read(statement: &Statement, index: T) -> Result { 504 | use std::ptr::copy_nonoverlapping as copy; 505 | unsafe { 506 | let pointer = 507 | ffi::sqlite3_column_blob(statement.raw.0, index.index(statement)? as c_int); 508 | if pointer.is_null() { 509 | return Ok(vec![]); 510 | } 511 | let count = ffi::sqlite3_column_bytes(statement.raw.0, index.index(statement)? as c_int) 512 | as usize; 513 | let mut buffer = Vec::with_capacity(count); 514 | copy(pointer as *const u8, buffer.as_mut_ptr(), count); 515 | buffer.set_len(count); 516 | Ok(buffer) 517 | } 518 | } 519 | } 520 | 521 | impl ReadableWithIndex for f64 { 522 | #[allow(clippy::unnecessary_cast)] 523 | fn read(statement: &Statement, index: T) -> Result { 524 | Ok(unsafe { 525 | ffi::sqlite3_column_double(statement.raw.0, index.index(statement)? as c_int) as f64 526 | }) 527 | } 528 | } 529 | 530 | impl ReadableWithIndex for i64 { 531 | #[allow(clippy::unnecessary_cast)] 532 | fn read(statement: &Statement, index: T) -> Result { 533 | Ok(unsafe { 534 | ffi::sqlite3_column_int64(statement.raw.0, index.index(statement)? as c_int) as i64 535 | }) 536 | } 537 | } 538 | 539 | impl ReadableWithIndex for String { 540 | fn read(statement: &Statement, index: T) -> Result { 541 | unsafe { 542 | let pointer = 543 | ffi::sqlite3_column_text(statement.raw.0, index.index(statement)? as c_int); 544 | if pointer.is_null() { 545 | raise!("cannot read a text column"); 546 | } 547 | Ok(c_str_to_string!(pointer)) 548 | } 549 | } 550 | } 551 | 552 | impl ReadableWithIndex for Value { 553 | fn read(statement: &Statement, index: T) -> Result { 554 | Ok(match statement.column_type(index)? { 555 | Type::Binary => Value::Binary(ReadableWithIndex::read(statement, index)?), 556 | Type::Float => Value::Float(ReadableWithIndex::read(statement, index)?), 557 | Type::Integer => Value::Integer(ReadableWithIndex::read(statement, index)?), 558 | Type::String => Value::String(ReadableWithIndex::read(statement, index)?), 559 | Type::Null => Value::Null, 560 | }) 561 | } 562 | } 563 | 564 | impl ReadableWithIndex for Option { 565 | fn read(statement: &Statement, index: U) -> Result { 566 | if statement.column_type(index)? == Type::Null { 567 | Ok(None) 568 | } else { 569 | T::read(statement, index).map(Some) 570 | } 571 | } 572 | } 573 | 574 | pub fn new<'l, T>(raw_connection: *mut ffi::sqlite3, statement: T) -> Result> 575 | where 576 | T: AsRef, 577 | { 578 | let mut raw_statement = std::ptr::null_mut(); 579 | unsafe { 580 | ok!( 581 | raw_connection, 582 | ffi::sqlite3_prepare_v2( 583 | raw_connection, 584 | str_to_cstr!(statement.as_ref()).as_ptr(), 585 | -1, 586 | &mut raw_statement, 587 | std::ptr::null_mut(), 588 | ) 589 | ); 590 | } 591 | let column_count = unsafe { ffi::sqlite3_column_count(raw_statement) as usize }; 592 | let column_names = (0..column_count) 593 | .map(|index| unsafe { 594 | let raw = ffi::sqlite3_column_name(raw_statement, index as c_int); 595 | debug_assert!(!raw.is_null()); 596 | c_str_to_str!(raw).unwrap().to_string() 597 | }) 598 | .collect::>(); 599 | let column_mapping = column_names 600 | .iter() 601 | .enumerate() 602 | .map(|(index, name)| (name.to_string(), index)) 603 | .collect(); 604 | Ok(Statement { 605 | raw: (raw_statement, raw_connection), 606 | column_names: Rc::new(column_names), 607 | column_mapping: Rc::new(column_mapping), 608 | phantom: PhantomData, 609 | }) 610 | } 611 | --------------------------------------------------------------------------------