1use crate::config::{SQLITE_URI, VFS_NAME};
7use crate::db::row::Row;
8use crate::db::statement::Statement;
9use crate::db::value::{ToSql, Value};
10use crate::db::{pragmas, DbError};
11use crate::sqlite_vfs::ffi;
12use std::ffi::{c_char, c_int, c_void, CStr, CString};
13use std::ptr::{self, NonNull};
14
15pub struct Connection {
16 raw: NonNull<ffi::sqlite3>,
17}
18
19pub fn open_read_write() -> Result<Connection, DbError> {
20 let flags = ffi::SQLITE_OPEN_READWRITE
21 | ffi::SQLITE_OPEN_CREATE
22 | ffi::SQLITE_OPEN_URI
23 | ffi::SQLITE_OPEN_NOMUTEX;
24 let connection = Connection::open(flags)?;
25 pragmas::apply_read_write(&connection)?;
26 Ok(connection)
27}
28
29pub fn open_read_only() -> Result<Connection, DbError> {
30 let flags = ffi::SQLITE_OPEN_READONLY | ffi::SQLITE_OPEN_URI | ffi::SQLITE_OPEN_NOMUTEX;
31 let connection = Connection::open(flags)?;
32 pragmas::apply_read_only(&connection)?;
33 Ok(connection)
34}
35
36impl Connection {
37 fn open(flags: c_int) -> Result<Self, DbError> {
38 let filename = CString::new(SQLITE_URI).map_err(|_| DbError::InteriorNul)?;
39 let vfs = CString::new(VFS_NAME).map_err(|_| DbError::InteriorNul)?;
40 let mut db = ptr::null_mut();
41 let rc = unsafe { ffi::sqlite3_open_v2(filename.as_ptr(), &mut db, flags, vfs.as_ptr()) };
42 let Some(raw) = NonNull::new(db) else {
43 return Err(DbError::Sqlite(
44 rc,
45 "sqlite3_open_v2 returned null".to_string(),
46 ));
47 };
48 if rc != ffi::SQLITE_OK {
49 let error = sqlite_error(raw.as_ptr(), rc);
50 unsafe {
51 ffi::sqlite3_close(raw.as_ptr());
52 }
53 return Err(error);
54 }
55 Ok(Self { raw })
56 }
57
58 pub fn raw(&self) -> *mut ffi::sqlite3 {
59 self.raw.as_ptr()
60 }
61
62 pub fn execute_batch(&self, sql: &str) -> Result<(), DbError> {
63 let sql = CString::new(sql).map_err(|_| DbError::InteriorNul)?;
64 let mut error = ptr::null_mut();
65 let rc = unsafe {
66 ffi::sqlite3_exec(
67 self.raw.as_ptr(),
68 sql.as_ptr(),
69 None,
70 ptr::null_mut(),
71 &mut error,
72 )
73 };
74 if rc == ffi::SQLITE_OK {
75 return Ok(());
76 }
77 Err(classify_sqlite_error(rc, take_error_message(error)))
78 }
79
80 pub fn execute(&self, sql: &str, values: &[&dyn ToSql]) -> Result<(), DbError> {
81 let mut statement = self.prepare(sql)?;
82 statement.execute(values)
83 }
84
85 pub fn execute_named(&self, sql: &str, values: &[(&str, &dyn ToSql)]) -> Result<(), DbError> {
86 let mut statement = self.prepare(sql)?;
87 statement.execute_named(values)
88 }
89
90 pub fn execute_with_texts(&self, sql: &str, values: &[&str]) -> Result<(), DbError> {
91 let values = values
92 .iter()
93 .map(|value| value as &dyn ToSql)
94 .collect::<Vec<_>>();
95 self.execute(sql, &values)
96 }
97
98 pub fn prepare(&self, sql: &str) -> Result<Statement<'_>, DbError> {
99 let sql = CString::new(sql).map_err(|_| DbError::InteriorNul)?;
100 let mut statement = ptr::null_mut();
101 let rc = unsafe {
102 ffi::sqlite3_prepare_v2(
103 self.raw.as_ptr(),
104 sql.as_ptr(),
105 -1,
106 &mut statement,
107 ptr::null_mut(),
108 )
109 };
110 if rc != ffi::SQLITE_OK {
111 return Err(sqlite_error(self.raw.as_ptr(), rc));
112 }
113 let Some(raw) = NonNull::new(statement) else {
114 return Err(DbError::Sqlite(
115 rc,
116 "sqlite3_prepare_v2 returned null".to_string(),
117 ));
118 };
119 Ok(Statement::new(self.raw.as_ptr(), raw))
120 }
121
122 pub fn query_one<T, F>(&self, sql: &str, values: &[&dyn ToSql], f: F) -> Result<T, DbError>
123 where
124 F: FnOnce(&Row<'_>) -> Result<T, DbError>,
125 {
126 let mut statement = self.prepare(sql)?;
127 statement.query_one(values, f)
128 }
129
130 pub fn query_one_named<T, F>(
131 &self,
132 sql: &str,
133 values: &[(&str, &dyn ToSql)],
134 f: F,
135 ) -> Result<T, DbError>
136 where
137 F: FnOnce(&Row<'_>) -> Result<T, DbError>,
138 {
139 let mut statement = self.prepare(sql)?;
140 statement.query_one_named(values, f)
141 }
142
143 pub fn query_optional<T, F>(
144 &self,
145 sql: &str,
146 values: &[&dyn ToSql],
147 f: F,
148 ) -> Result<Option<T>, DbError>
149 where
150 F: FnOnce(&Row<'_>) -> Result<T, DbError>,
151 {
152 let mut statement = self.prepare(sql)?;
153 statement.query_optional(values, f)
154 }
155
156 pub fn query_optional_named<T, F>(
157 &self,
158 sql: &str,
159 values: &[(&str, &dyn ToSql)],
160 f: F,
161 ) -> Result<Option<T>, DbError>
162 where
163 F: FnOnce(&Row<'_>) -> Result<T, DbError>,
164 {
165 let mut statement = self.prepare(sql)?;
166 statement.query_optional_named(values, f)
167 }
168
169 pub fn query_all<T, F>(&self, sql: &str, values: &[&dyn ToSql], f: F) -> Result<Vec<T>, DbError>
170 where
171 F: FnMut(&Row<'_>) -> Result<T, DbError>,
172 {
173 let mut statement = self.prepare(sql)?;
174 statement.query_all(values, f)
175 }
176
177 pub fn query_all_named<T, F>(
178 &self,
179 sql: &str,
180 values: &[(&str, &dyn ToSql)],
181 f: F,
182 ) -> Result<Vec<T>, DbError>
183 where
184 F: FnMut(&Row<'_>) -> Result<T, DbError>,
185 {
186 let mut statement = self.prepare(sql)?;
187 statement.query_all_named(values, f)
188 }
189
190 pub fn exists(&self, sql: &str, values: &[&dyn ToSql]) -> Result<bool, DbError> {
191 self.query_optional(sql, values, |row| row.get::<i64>(0))
192 .map(|value| value.unwrap_or(0) != 0)
193 }
194
195 pub fn query_i64(&self, sql: &str) -> Result<i64, DbError> {
196 self.query_one(sql, &[], |row| row.get(0))
197 }
198
199 pub fn query_string(&self, sql: &str) -> Result<String, DbError> {
200 self.query_one(sql, &[], |row| row.get(0))
201 }
202
203 pub fn query_optional_string_with_text(
204 &self,
205 sql: &str,
206 value: &str,
207 ) -> Result<Option<String>, DbError> {
208 let value = Value::Text(value);
209 self.query_optional(sql, &[&value], |row| row.get(0))
210 }
211}
212
213impl Drop for Connection {
214 fn drop(&mut self) {
215 unsafe {
216 ffi::sqlite3_close(self.raw.as_ptr());
217 }
218 }
219}
220
221pub(crate) fn sqlite_error(db: *mut ffi::sqlite3, code: c_int) -> DbError {
222 let message = unsafe {
223 let ptr = ffi::sqlite3_errmsg(db);
224 if ptr.is_null() {
225 "unknown sqlite error".to_string()
226 } else {
227 CStr::from_ptr(ptr).to_string_lossy().into_owned()
228 }
229 };
230 classify_sqlite_error(code, message)
231}
232
233fn classify_sqlite_error(code: c_int, message: String) -> DbError {
234 if code == ffi::SQLITE_CONSTRAINT {
235 DbError::Constraint(message)
236 } else {
237 DbError::Sqlite(code, message)
238 }
239}
240
241fn take_error_message(error: *mut c_char) -> String {
242 if error.is_null() {
243 return "unknown sqlite error".to_string();
244 }
245 let message = unsafe { CStr::from_ptr(error).to_string_lossy().into_owned() };
246 unsafe {
247 ffi::sqlite3_free(error.cast::<c_void>());
248 }
249 message
250}