Skip to main content

sqlite3_ext/
globals.rs

1use super::{ffi, sqlite3_match_version, sqlite3_require_version, types::*};
2use std::{cmp::Ordering, ffi::CStr, str};
3
4/// The version of SQLite.
5pub struct SqliteVersion;
6
7/// The version of SQLite. See [SqliteVersion] for details.
8pub static SQLITE_VERSION: SqliteVersion = SqliteVersion;
9
10impl SqliteVersion {
11    /// Returns the numeric version of SQLite.
12    ///
13    /// The format of this value is the semantic version with a simple encoding: `major *
14    /// 1000000 + minor * 1000 + patch`. For example, SQLite version 3.8.2 is encoded as
15    /// `3_008_002`.
16    pub fn as_i32(&self) -> i32 {
17        unsafe { ffi::sqlite3_libversion_number() }
18    }
19
20    /// Returns the human-readable version of SQLite. Example: `"3.8.2"`.
21    pub fn as_str(&self) -> &'static str {
22        let ret = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) };
23        ret.to_str().expect("sqlite3_libversion")
24    }
25
26    /// Returns a hash of the SQLite source code. The objective is to detect accidental and/or
27    /// careless edits. A forger can subvert this feature.
28    ///
29    /// Requires SQLite 3.21.0.
30    pub fn sourceid(&self) -> Result<&'static str> {
31        sqlite3_require_version!(3_021_000, {
32            let ret = unsafe { CStr::from_ptr(ffi::sqlite3_sourceid()) };
33            Ok(ret.to_str().expect("sqlite3_sourceid"))
34        })
35    }
36}
37
38impl std::fmt::Display for SqliteVersion {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        self.as_str().fmt(f)
41    }
42}
43
44/// Perform a case-insensitive comparison using the same collation that SQLite uses.
45///
46/// This interface was published in SQLite 3.6.17. On earlier versions of SQLite, this method
47/// emulates the SQLite behavior.
48pub fn sqlite3_stricmp(a: &str, b: &str) -> Ordering {
49    sqlite3_match_version! {
50        3_006_017 => {
51            let rc = unsafe {
52                ffi::sqlite3_strnicmp(a.as_ptr() as _, b.as_ptr() as _, std::cmp::min(a.len(), b.len()) as _)
53            };
54            if rc < 0 {
55                Ordering::Less
56            } else if rc > 0 {
57                Ordering::Greater
58            } else {
59                Ordering::Equal
60            }
61        }
62        _ => a
63            .bytes()
64            .zip(b.bytes())
65            .find_map(|(a, b)| match a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase()) {
66                Ordering::Equal => None,
67                x => Some(x),
68            })
69            .unwrap_or(a.len().cmp(&b.len())),
70    }
71}
72
73/// Perform an SQL [GLOB](https://www.sqlite.org/lang_expr.html#like) operation.
74///
75/// Requires SQLite 3.7.17.
76pub fn sqlite3_strglob(pattern: impl Into<Vec<u8>>, input: impl Into<Vec<u8>>) -> Result<bool> {
77    let _ = (&pattern, &input);
78    sqlite3_require_version!(3_007_017, {
79        let pattern = std::ffi::CString::new(pattern)?;
80        let input = std::ffi::CString::new(input)?;
81        Ok(unsafe { ffi::sqlite3_strglob(pattern.as_ptr(), input.as_ptr()) == 0 })
82    })
83}
84
85/// Perform an SQL [LIKE](https://www.sqlite.org/lang_expr.html#like) operation. The escape
86/// parameter can be 0 or any ASCII character. If the escape parameter is not ASCII, it is
87/// treated as though 0 were specified (no escape).
88///
89/// Requires SQLite 3.10.0.
90pub fn sqlite3_strlike(
91    pattern: impl Into<Vec<u8>>,
92    input: impl Into<Vec<u8>>,
93    escape: impl Into<char>,
94) -> Result<bool> {
95    let _ = (&pattern, &input, &escape);
96    sqlite3_require_version!(3_010_000, {
97        let pattern = std::ffi::CString::new(pattern)?;
98        let input = std::ffi::CString::new(input)?;
99        let escape = escape.into();
100        let escape: u32 = if escape.is_ascii() { escape as _ } else { 0 };
101        Ok(unsafe { ffi::sqlite3_strlike(pattern.as_ptr(), input.as_ptr(), escape) == 0 })
102    })
103}
104
105pub fn sqlite3_randomness(n: usize) -> Vec<u8> {
106    let mut ret = vec![0; n];
107    unsafe { ffi::sqlite3_randomness(n as _, ret.as_mut_ptr() as _) };
108    ret
109}
110
111#[cfg(all(test, feature = "static"))]
112mod test {
113    use super::*;
114
115    #[test]
116    fn version() -> Result<()> {
117        assert!(SqliteVersion.as_i32() > 3_000_000);
118        // "3.0.0" is the shortest posible version string
119        assert!(format!("{}", SqliteVersion).len() >= 5);
120        sqlite3_match_version! {
121            3_021_000 => assert!(SqliteVersion.sourceid()?.len() > 0),
122            _ => (),
123        }
124        Ok(())
125    }
126
127    #[test]
128    fn strings() -> Result<()> {
129        assert_eq!(sqlite3_stricmp("FOO", "bar"), Ordering::Greater);
130        assert_eq!(sqlite3_stricmp("bar", "FOO"), Ordering::Less);
131        assert_eq!(sqlite3_stricmp("bar", "BAR"), Ordering::Equal);
132        sqlite3_match_version! {
133            3_007_017 => assert_eq!(sqlite3_strglob("a/**/b", "a/c/d/e/f/b"), Ok(true)),
134            _ => (),
135        }
136        sqlite3_match_version! {
137            3_010_000 => {
138                assert_eq!(sqlite3_strlike("FOO\\_BAR", "FOO_BAR", '\\'), Ok(true));
139                assert_eq!(sqlite3_strlike("FOO_BAR", "FOOXBAR", 0), Ok(true));
140            }
141            _ => (),
142        }
143        Ok(())
144    }
145
146    #[test]
147    fn randomness() {
148        let ret = sqlite3_randomness(32);
149        assert_eq!(ret.len(), 32);
150        assert_ne!(ret, vec![0; 32]);
151    }
152}