sqlite_ll/
connection.rs

1use core::ffi::CStr;
2use core::ffi::{c_int, c_uint, c_void};
3use core::mem::MaybeUninit;
4use core::ops::BitOr;
5use core::ptr;
6use core::ptr::NonNull;
7
8#[cfg(feature = "std")]
9use std::path::Path;
10
11use crate::State;
12use crate::error::{Error, Result};
13use crate::owned::Owned;
14use crate::statement::Statement;
15use crate::utils::sqlite3_try;
16
17use sqlite3_sys as ffi;
18
19/// A collection of flags use to prepare a statement.
20pub struct Prepare(c_uint);
21
22impl Prepare {
23    /// No flags.
24    ///
25    /// This provides the default behavior when preparing a statement.
26    pub const EMPTY: Self = Self(0);
27
28    /// The PERSISTENT flag is a hint to the query planner that the prepared
29    /// statement will be retained for a long time and probably reused many
30    /// times. Without this flag, sqlite3_prepare_v3() and
31    /// sqlite3_prepare16_v3() assume that the prepared statement will be used
32    /// just once or at most a few times and then destroyed using
33    /// sqlite3_finalize() relatively soon. The current implementation acts on
34    /// this hint by avoiding the use of lookaside memory so as not to deplete
35    /// the limited store of lookaside memory. Future versions of SQLite may act
36    /// on this hint differently.
37    pub const PERSISTENT: Self = Self(ffi::SQLITE_PREPARE_PERSISTENT as c_uint);
38
39    /// The NORMALIZE flag is a no-op. This flag used to be required for any
40    /// prepared statement that wanted to use the sqlite3_normalized_sql()
41    /// interface. However, the sqlite3_normalized_sql() interface is now
42    /// available to all prepared statements, regardless of whether or not they
43    /// use this flag.
44    pub const NORMALIZE: Self = Self(ffi::SQLITE_PREPARE_NORMALIZE as c_uint);
45
46    /// The NO_VTAB flag causes the SQL compiler to return an error if the
47    /// statement uses any virtual tables.
48    pub const NO_VTAB: Self = Self(ffi::SQLITE_PREPARE_NO_VTAB as c_uint);
49}
50
51impl BitOr for Prepare {
52    type Output = Self;
53
54    fn bitor(self, rhs: Self) -> Self::Output {
55        Self(self.0 | rhs.0)
56    }
57}
58
59/// A sqlite database connection.
60///
61/// Connections are not thread-safe objects.
62///
63/// # Examples
64///
65/// Opening a connection to a filesystem path:
66///
67/// ```no_run
68/// use sqlite_ll::Connection;
69///
70/// let c = Connection::open("database.db")?;
71/// c.execute("CREATE TABLE test (id INTEGER);")?;
72/// # Ok::<_, sqlite_ll::Error>(())
73/// ```
74///
75/// Opening an in-memory database:
76///
77/// ```
78/// use sqlite_ll::Connection;
79///
80/// let c = Connection::open_memory()?;
81/// c.execute("CREATE TABLE test (id INTEGER);")?;
82/// # Ok::<_, sqlite_ll::Error>(())
83/// ```
84pub struct Connection {
85    raw: NonNull<ffi::sqlite3>,
86    busy_callback: Option<Owned>,
87}
88
89/// Connection is `Send`.
90unsafe impl Send for Connection {}
91
92impl Connection {
93    /// Open a database to the given path.
94    ///
95    /// Note that it is possible to open an in-memory database by passing
96    /// `":memory:"` here, this call might require allocating depending on the
97    /// platform, so it should be avoided in favor of using [`memory`]. To avoid
98    /// allocating for regular paths, you can use [`open_c_str`], however you
99    /// are responsible for ensuring the c-string is a valid path.
100    ///
101    /// This is the same as calling
102    /// `OpenOptions::new().read_write().create().open(path)`.
103    ///
104    /// [`memory`]: Self::memory
105    /// [`open_c_str`]: Self::open_c_str
106    #[cfg(feature = "std")]
107    #[cfg_attr(docsrs, cfg(feature = "std"))]
108    #[inline]
109    pub fn open(path: impl AsRef<Path>) -> Result<Connection> {
110        OpenOptions::new().read_write().create().open(path)
111    }
112
113    /// Open a database connection with a raw c-string.
114    ///
115    /// This can be used to open in-memory databases by passing `c":memory:"` or
116    /// a regular open call with a filesystem path like
117    /// `c"/path/to/database.sql"`.
118    ///
119    /// This is the same as calling
120    /// `OpenOptions::new().read_write().create().open_c_str(name)`.
121    #[inline]
122    pub fn open_c_str(name: &CStr) -> Result<Connection> {
123        OpenOptions::new().read_write().create().open_c_str(name)
124    }
125
126    /// Open an in-memory database.
127    ///
128    /// This is the same as calling
129    /// `OpenOptions::new().read_write().create().open_memory()`.
130    #[inline]
131    pub fn open_memory() -> Result<Connection> {
132        OpenOptions::new().read_write().create().open_memory()
133    }
134
135    /// Execute a statement without processing the resulting rows if any.
136    #[inline]
137    pub fn execute(&self, stmt: impl AsRef<str>) -> Result<()> {
138        self._execute(stmt.as_ref())
139    }
140
141    fn _execute(&self, stmt: &str) -> Result<()> {
142        unsafe {
143            let mut ptr = stmt.as_ptr().cast();
144            let mut len = stmt.len();
145
146            while len > 0 {
147                let mut raw = MaybeUninit::uninit();
148                let mut rest = MaybeUninit::uninit();
149
150                let l = i32::try_from(len).unwrap_or(i32::MAX);
151
152                let res = ffi::sqlite3_prepare_v3(
153                    self.raw.as_ptr(),
154                    ptr,
155                    l,
156                    0,
157                    raw.as_mut_ptr(),
158                    rest.as_mut_ptr(),
159                );
160
161                if res != ffi::SQLITE_OK {
162                    return Err(Error::new(ffi::sqlite3_errcode(self.raw.as_ptr())));
163                }
164
165                let rest = rest.assume_init();
166
167                // If statement is null then it's simply empty, so we can safely
168                // skip it, otherwise iterate over all rows.
169                if let Some(raw) = NonNull::new(raw.assume_init()) {
170                    let mut statement = Statement::from_raw(raw);
171                    while let State::Row = statement.step()? {}
172                }
173
174                // Skip over empty statements.
175                let o = rest.offset_from_unsigned(ptr);
176                len -= o;
177                ptr = rest;
178            }
179
180            Ok(())
181        }
182    }
183
184    /// Build a prepared statement.
185    ///
186    /// This is the same as calling `prepare_with` with `Prepare::EMPTY`.
187    ///
188    /// The database connection will be kept open for the lifetime of this
189    /// statement.
190    ///
191    /// # Errors
192    ///
193    /// If the prepare call contains multiple statements, it will error. To
194    /// execute multiple statements, use [`execute`] instead.
195    ///
196    /// ```
197    /// use sqlite_ll::{Connection, Code};
198    ///
199    /// let c = Connection::open_memory()?;
200    ///
201    /// let e = c.prepare(
202    ///     "
203    ///     CREATE TABLE test (id INTEGER) /* test */;
204    ///     INSERT INTO test (id) VALUES (1);
205    ///     "
206    /// ).unwrap_err();
207    ///
208    /// assert_eq!(e.code(), Code::ERROR);
209    /// # Ok::<_, sqlite_ll::Error>(())
210    /// ```
211    ///
212    /// [`execute`]: Self::execute
213    ///
214    /// # Examples
215    ///
216    /// ```
217    /// use sqlite_ll::{Connection, State, Prepare};
218    ///
219    /// let c = Connection::open_memory()?;
220    /// c.execute("CREATE TABLE test (id INTEGER);")?;
221    ///
222    /// let mut insert_stmt = c.prepare("INSERT INTO test (id) VALUES (?);")?;
223    /// let mut query_stmt = c.prepare("SELECT id FROM test;")?;
224    ///
225    /// drop(c);
226    ///
227    /// insert_stmt.reset()?;
228    /// insert_stmt.bind(1, 42)?;
229    /// assert_eq!(insert_stmt.step()?, State::Done);
230    ///
231    /// query_stmt.reset()?;
232    ///
233    /// while let State::Row = query_stmt.step()? {
234    ///     let id: i64 = query_stmt.read(0)?;
235    ///     assert_eq!(id, 42);
236    /// }
237    /// # Ok::<_, sqlite_ll::Error>(())
238    /// ```
239    #[inline]
240    pub fn prepare(&self, stmt: impl AsRef<str>) -> Result<Statement> {
241        self.prepare_with(stmt, Prepare::EMPTY)
242    }
243
244    /// Build a prepared statement with custom flags.
245    ///
246    /// For long-running statements it is recommended that they have the
247    /// [`Prepare::PERSISTENT`] flag set.
248    ///
249    /// The database connection will be kept open for the lifetime of this
250    /// statement.
251    ///
252    /// # Errors
253    ///
254    /// If the prepare call contains multiple statements, it will error. To
255    /// execute multiple statements, use [`execute`] instead.
256    ///
257    /// ```
258    /// use sqlite_ll::{Connection, Code, Prepare};
259    ///
260    /// let c = Connection::open_memory()?;
261    ///
262    /// let e = c.prepare_with(
263    ///     "
264    ///     CREATE TABLE test (id INTEGER) /* test */;
265    ///     INSERT INTO test (id) VALUES (1);
266    ///     ",
267    ///     Prepare::PERSISTENT
268    /// ).unwrap_err();
269    /// assert_eq!(e.code(), Code::ERROR);
270    /// # Ok::<_, sqlite_ll::Error>(())
271    /// ```
272    ///
273    /// [`execute`]: Self::execute
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// use sqlite_ll::{Connection, State, Prepare};
279    ///
280    /// let c = Connection::open_memory()?;
281    /// c.execute("CREATE TABLE test (id INTEGER);")?;
282    ///
283    /// let mut insert_stmt = c.prepare_with("INSERT INTO test (id) VALUES (?);", Prepare::PERSISTENT)?;
284    /// let mut query_stmt = c.prepare_with("SELECT id FROM test;", Prepare::PERSISTENT)?;
285    ///
286    /// drop(c);
287    ///
288    /// /* .. */
289    ///
290    /// insert_stmt.reset()?;
291    /// insert_stmt.bind(1, 42)?;
292    /// assert_eq!(insert_stmt.step()?, State::Done);
293    ///
294    /// query_stmt.reset()?;
295    ///
296    /// while let State::Row = query_stmt.step()? {
297    ///     let id: i64 = query_stmt.read(0)?;
298    ///     assert_eq!(id, 42);
299    /// }
300    /// # Ok::<_, sqlite_ll::Error>(())
301    /// ```
302    pub fn prepare_with(&self, stmt: impl AsRef<str>, flags: Prepare) -> Result<Statement> {
303        let stmt = stmt.as_ref();
304
305        unsafe {
306            let mut raw = MaybeUninit::uninit();
307            let mut rest = MaybeUninit::uninit();
308
309            let ptr = stmt.as_ptr().cast();
310            let len = i32::try_from(stmt.len()).unwrap_or(i32::MAX);
311
312            sqlite3_try! {
313                ffi::sqlite3_prepare_v3(
314                    self.raw.as_ptr(),
315                    ptr,
316                    len,
317                    flags.0,
318                    raw.as_mut_ptr(),
319                    rest.as_mut_ptr(),
320                )
321            };
322
323            let rest = rest.assume_init();
324
325            let o = rest.offset_from_unsigned(ptr);
326
327            if o != stmt.len() {
328                return Err(Error::new(ffi::SQLITE_ERROR));
329            }
330
331            let raw = ptr::NonNull::new_unchecked(raw.assume_init());
332            Ok(Statement::from_raw(raw))
333        }
334    }
335
336    /// Return the number of rows inserted, updated, or deleted by the most
337    /// recent INSERT, UPDATE, or DELETE statement.
338    #[inline]
339    pub fn change_count(&self) -> usize {
340        unsafe { ffi::sqlite3_changes(self.raw.as_ptr()) as usize }
341    }
342
343    /// Return the total number of rows inserted, updated, and deleted by all
344    /// INSERT, UPDATE, and DELETE statements since the connection was opened.
345    #[inline]
346    pub fn total_change_count(&self) -> usize {
347        unsafe { ffi::sqlite3_total_changes(self.raw.as_ptr()) as usize }
348    }
349
350    /// Set a callback for handling busy events.
351    ///
352    /// The callback is triggered when the database cannot perform an operation
353    /// due to processing of some other request. If the callback returns `true`,
354    /// the operation will be repeated.
355    pub fn set_busy_handler<F>(&mut self, callback: F) -> Result<()>
356    where
357        F: FnMut(usize) -> bool + Send + 'static,
358    {
359        self.remove_busy_handler()?;
360
361        unsafe {
362            let callback = Owned::new(callback)?;
363
364            let result = ffi::sqlite3_busy_handler(
365                self.raw.as_ptr(),
366                Some(busy_callback::<F>),
367                callback.as_ptr().cast(),
368            );
369
370            self.busy_callback = Some(callback);
371            sqlite3_try!(result);
372        }
373
374        Ok(())
375    }
376
377    /// Set an implicit callback for handling busy events that tries to repeat
378    /// rejected operations until a timeout expires.
379    #[inline]
380    pub fn set_busy_timeout(&mut self, ms: c_int) -> Result<()> {
381        unsafe {
382            sqlite3_try! {
383                ffi::sqlite3_busy_timeout(
384                    self.raw.as_ptr(),
385                    ms
386                )
387            };
388        }
389
390        Ok(())
391    }
392
393    /// Remove the callback handling busy events.
394    #[inline]
395    pub fn remove_busy_handler(&mut self) -> Result<()> {
396        unsafe {
397            sqlite3_try! {
398                ffi::sqlite3_busy_handler(
399                    self.raw.as_ptr(),
400                    None,
401                    ptr::null_mut()
402                )
403            };
404        }
405
406        self.busy_callback = None;
407        Ok(())
408    }
409}
410
411impl Drop for Connection {
412    #[inline]
413    #[allow(unused_must_use)]
414    fn drop(&mut self) {
415        self.remove_busy_handler();
416
417        // Will close the connection unconditionally. The database will stay
418        // alive until all associated prepared statements have been closed since
419        // we're using v2.
420        let code = unsafe { ffi::sqlite3_close_v2(self.raw.as_ptr()) };
421        debug_assert_eq!(code, sqlite3_sys::SQLITE_OK);
422    }
423}
424
425/// Options that can be used to customize the opening of a SQLite database.
426///
427/// By default the database is opened in multi-threaded mode with the
428/// [`SQLITE_OPEN_NOMUTEX`] set to ensure that [`Statement`] objects can be used
429/// separately from the connection that constructed them.
430///
431/// To avoid this overhead in case you know how the statements will be used, you
432/// can unset this by calling [`unset_no_mutex`].
433///
434/// [`unset_no_mutex`]: Self::unset_no_mutex
435/// [`SQLITE_OPEN_NOMUTEX`]: https://sqlite.org/c3ref/open.html
436#[derive(Clone, Copy, Debug)]
437pub struct OpenOptions {
438    raw: c_int,
439}
440
441impl OpenOptions {
442    /// Create flags for opening a database connection.
443    #[inline]
444    pub fn new() -> Self {
445        Self {
446            raw: ffi::SQLITE_OPEN_NOMUTEX,
447        }
448    }
449
450    /// The database is opened in read-only mode. If the database does not
451    /// already exist, an error is returned.
452    #[inline]
453    pub fn read_only(mut self) -> Self {
454        self.raw |= ffi::SQLITE_OPEN_READONLY;
455        self
456    }
457
458    /// The database is opened for reading and writing if possible, or reading
459    /// only if the file is write protected by the operating system. In either
460    /// case the database must already exist, otherwise an error is returned.
461    /// For historical reasons, if opening in read-write mode fails due to
462    /// OS-level permissions, an attempt is made to open it in read-only mode.
463    /// sqlite3_db_readonly() can be used to determine whether the database is
464    /// actually read-write.
465    #[inline]
466    pub fn read_write(mut self) -> Self {
467        self.raw |= ffi::SQLITE_OPEN_READWRITE;
468        self
469    }
470
471    /// The database is opened for reading and writing, and is created if it
472    /// does not already exist.
473    ///
474    /// Note that a mode option like [`read_write`] must be set, otherwise this
475    /// will cause an error when opening.
476    ///
477    /// [`read_write`]: Self::read_write
478    #[inline]
479    pub fn create(mut self) -> Self {
480        self.raw |= ffi::SQLITE_OPEN_CREATE;
481        self
482    }
483
484    /// The filename can be interpreted as a URI if this flag is set.
485    #[inline]
486    pub fn uri(mut self) -> Self {
487        self.raw |= ffi::SQLITE_OPEN_URI;
488        self
489    }
490
491    /// The database will be opened as an in-memory database. The database is
492    /// named by the "filename" argument for the purposes of cache-sharing, if
493    /// shared cache mode is enabled, but the "filename" is otherwise ignored.
494    #[inline]
495    pub fn memory(mut self) -> Self {
496        self.raw |= ffi::SQLITE_OPEN_MEMORY;
497        self
498    }
499
500    /// The new database connection will use the "multi-thread" [threading mode].
501    /// This means that separate threads are allowed to use SQLite at the same
502    /// time, as long as each thread is using a different database connection.
503    ///
504    /// [threading mode]: https://www.sqlite.org/threadsafe.html
505    #[inline]
506    pub fn no_mutex(mut self) -> Self {
507        self.raw |= ffi::SQLITE_OPEN_NOMUTEX;
508        self
509    }
510
511    /// The new database connection will use the "serialized" [threading mode].
512    /// This means the multiple threads can safely attempt to use the same
513    /// database connection at the same time. Mutexes will block any actual
514    /// concurrency, but in this mode there is no harm in trying.
515    ///
516    /// [threading mode]: https://sqlite.org/threadsafe.html
517    #[inline]
518    pub fn full_mutex(mut self) -> Self {
519        self.raw |= ffi::SQLITE_OPEN_FULLMUTEX;
520        self
521    }
522
523    /// Set the database to be opened without the "multi-thread" [threading
524    /// mode]. The effect of calling this is that [`open_full_mutex`] and
525    /// [`open_no_mutex`] are unset.
526    ///
527    /// [threading mode]: https://www.sqlite.org/threadsafe.html
528    ///
529    /// # Safety
530    ///
531    /// Prepared statements being separated from the connection object rely on
532    /// sqlite being in a multi-threaded mode to work safely. Unsetting this
533    /// option means you take responsibility for ensuring that no two threads
534    /// access the same connection even indirectly through [`Statement`]
535    /// simultaneously.
536    #[inline]
537    pub unsafe fn unsynchronized(mut self) -> Self {
538        self.raw &= !(ffi::SQLITE_OPEN_NOMUTEX | ffi::SQLITE_OPEN_FULLMUTEX);
539        self
540    }
541
542    /// The database is opened with shared cache enabled, overriding the default
543    /// shared cache setting provided. The use of shared cache mode is
544    /// discouraged and hence shared cache capabilities may be omitted from many
545    /// builds of SQLite. In such cases, this option is a no-op.
546    #[inline]
547    pub fn shared_cache(mut self) -> Self {
548        self.raw |= ffi::SQLITE_OPEN_SHAREDCACHE;
549        self
550    }
551
552    /// The database is opened with shared cache disabled, overriding the
553    /// default shared cache setting provided.
554    #[inline]
555    pub fn private_cache(mut self) -> Self {
556        self.raw |= ffi::SQLITE_OPEN_PRIVATECACHE;
557        self
558    }
559
560    /// The database connection comes up in "extended result code mode". In
561    /// other words, the database behaves as if
562    /// [`Connection::extended_result_codes`] were called on the database
563    /// connection as soon as the connection is created. In addition to setting
564    /// the extended result code mode, this flag also causes the [`open`] call
565    /// to return an extended result code.
566    ///
567    /// [`open`]: Self::open
568    #[inline]
569    pub fn extended_result_code(mut self) -> Self {
570        self.raw |= ffi::SQLITE_OPEN_EXRESCODE;
571        self
572    }
573
574    /// The database filename is not allowed to contain a symbolic link.
575    #[inline]
576    pub fn no_follow(mut self) -> Self {
577        self.raw |= ffi::SQLITE_OPEN_NOFOLLOW;
578        self
579    }
580
581    /// Open a database to the given path.
582    ///
583    /// Note that it is possible to open an in-memory database by passing
584    /// `":memory:"` here, this call might require allocating depending on the
585    /// platform, so it should be avoided in favor of using [`memory`]. To avoid
586    /// allocating for regular paths, you can use [`open_c_str`], however you
587    /// are responsible for ensuring the c-string is a valid path.
588    ///
589    /// [`memory`]: Self::memory
590    /// [`open_c_str`]: Self::open_c_str
591    #[cfg(feature = "std")]
592    #[cfg_attr(docsrs, cfg(feature = "std"))]
593    pub fn open(&self, path: impl AsRef<Path>) -> Result<Connection> {
594        let path = crate::utils::path_to_cstring(path.as_ref())?;
595        self._open(&path)
596    }
597
598    /// Open a database connection with a raw c-string.
599    ///
600    /// This can be used to open in-memory databases by passing `c":memory:"` or
601    /// a regular open call with a filesystem path like
602    /// `c"/path/to/database.sql"`.
603    pub fn open_c_str(&self, name: &CStr) -> Result<Connection> {
604        self._open(name)
605    }
606
607    /// Open an in-memory database.
608    pub fn open_memory(&self) -> Result<Connection> {
609        self._open(c":memory:")
610    }
611
612    fn _open(&self, name: &CStr) -> Result<Connection> {
613        unsafe {
614            let mut raw = MaybeUninit::uninit();
615
616            let code = ffi::sqlite3_open_v2(name.as_ptr(), raw.as_mut_ptr(), self.raw, ptr::null());
617
618            let raw = raw.assume_init();
619
620            if code != ffi::SQLITE_OK {
621                ffi::sqlite3_close(raw);
622                return Err(Error::new(code));
623            }
624
625            Ok(Connection {
626                raw: NonNull::new_unchecked(raw),
627                busy_callback: None,
628            })
629        }
630    }
631}
632
633extern "C" fn busy_callback<F>(callback: *mut c_void, attempts: c_int) -> c_int
634where
635    F: FnMut(usize) -> bool,
636{
637    unsafe {
638        if (*(callback as *mut F))(attempts as usize) {
639            1
640        } else {
641            0
642        }
643    }
644}