libsql_sys/
statement.rs

1#![allow(clippy::missing_safety_doc)]
2
3use std::ffi::{c_char, c_int};
4use std::sync::atomic::AtomicBool;
5
6use crate::error::Result;
7
8#[derive(Debug)]
9pub struct Statement {
10    pub raw_stmt: *mut crate::ffi::sqlite3_stmt,
11    finalized: AtomicBool,
12    tail: usize,
13}
14
15// Safety: works as long as libSQL is compiled and set up with SERIALIZABLE threading model, which is the default.
16unsafe impl Sync for Statement {}
17
18// Safety: works as long as libSQL is compiled and set up with SERIALIZABLE threading model, which is the default.
19unsafe impl Send for Statement {}
20
21impl Drop for Statement {
22    fn drop(&mut self) {
23        self.finalize();
24    }
25}
26
27impl Statement {
28    pub fn finalize(&self) {
29        if !self
30            .finalized
31            .swap(true, std::sync::atomic::Ordering::SeqCst)
32        {
33            unsafe {
34                crate::ffi::sqlite3_finalize(self.raw_stmt);
35            }
36        }
37    }
38
39    pub fn bind_null(&self, idx: i32) {
40        unsafe {
41            crate::ffi::sqlite3_bind_null(self.raw_stmt, idx);
42        }
43    }
44
45    pub fn bind_int64(&self, idx: i32, value: i64) {
46        unsafe {
47            crate::ffi::sqlite3_bind_int64(self.raw_stmt, idx, value);
48        }
49    }
50
51    pub fn bind_double(&self, idx: i32, value: f64) {
52        unsafe {
53            crate::ffi::sqlite3_bind_double(self.raw_stmt, idx, value);
54        }
55    }
56
57    pub fn bind_text(&self, idx: i32, value: &[u8]) {
58        unsafe {
59            crate::ffi::sqlite3_bind_text(
60                self.raw_stmt,
61                idx,
62                value.as_ptr() as *const c_char,
63                value.len() as i32,
64                SQLITE_TRANSIENT(),
65            );
66        }
67    }
68
69    pub fn bind_blob(&self, idx: i32, value: &[u8]) {
70        unsafe {
71            crate::ffi::sqlite3_bind_blob(
72                self.raw_stmt,
73                idx,
74                value.as_ptr() as *const std::ffi::c_void,
75                value.len() as i32,
76                SQLITE_TRANSIENT(),
77            );
78        }
79    }
80
81    pub fn step(&self) -> std::ffi::c_int {
82        unsafe { crate::ffi::sqlite3_step(self.raw_stmt) }
83    }
84
85    pub fn interrupt(&self) {
86        unsafe { crate::ffi::libsql_stmt_interrupt(self.raw_stmt) }
87    }
88
89    pub fn reset(&self) -> std::ffi::c_int {
90        unsafe { crate::ffi::sqlite3_reset(self.raw_stmt) }
91    }
92
93    pub fn column_count(&self) -> i32 {
94        unsafe { crate::ffi::sqlite3_column_count(self.raw_stmt) }
95    }
96
97    pub fn column_value(&self, idx: i32) -> crate::Value {
98        let raw_value = unsafe { crate::ffi::sqlite3_column_value(self.raw_stmt, idx) };
99        crate::Value { raw_value }
100    }
101
102    pub fn column_type(&self, idx: i32) -> i32 {
103        unsafe { crate::ffi::sqlite3_column_type(self.raw_stmt, idx) }
104    }
105
106    pub fn column_name(&self, idx: i32) -> Option<&str> {
107        let raw_name = unsafe { crate::ffi::sqlite3_column_name(self.raw_stmt, idx) };
108
109        if raw_name.is_null() {
110            return None;
111        }
112
113        let raw_name = unsafe { std::ffi::CStr::from_ptr(raw_name as *const c_char) };
114        let raw_name = raw_name.to_str().unwrap();
115        Some(raw_name)
116    }
117
118    pub fn column_origin_name(&self, idx: i32) -> Option<&str> {
119        let raw_name = unsafe { crate::ffi::sqlite3_column_origin_name(self.raw_stmt, idx) };
120
121        if raw_name.is_null() {
122            return None;
123        }
124
125        let raw_name = unsafe { std::ffi::CStr::from_ptr(raw_name as *const c_char) };
126
127        let raw_name = raw_name.to_str().unwrap();
128        Some(raw_name)
129    }
130
131    pub fn column_table_name(&self, idx: i32) -> Option<&str> {
132        let raw_name = unsafe { crate::ffi::sqlite3_column_table_name(self.raw_stmt, idx) };
133        if raw_name.is_null() {
134            return None;
135        }
136        let raw_name = unsafe { std::ffi::CStr::from_ptr(raw_name as *const c_char) };
137        let raw_name = raw_name.to_str().unwrap();
138        Some(raw_name)
139    }
140
141    pub fn column_database_name(&self, idx: i32) -> Option<&str> {
142        let raw_name = unsafe { crate::ffi::sqlite3_column_database_name(self.raw_stmt, idx) };
143        if raw_name.is_null() {
144            return None;
145        }
146        let raw_name = unsafe { std::ffi::CStr::from_ptr(raw_name as *const c_char) };
147        let raw_name = raw_name.to_str().unwrap();
148        Some(raw_name)
149    }
150
151    pub fn column_decltype(&self, idx: i32) -> Option<&str> {
152        let raw_name = unsafe { crate::ffi::sqlite3_column_decltype(self.raw_stmt, idx) };
153        if raw_name.is_null() {
154            return None;
155        }
156        let raw_name = unsafe { std::ffi::CStr::from_ptr(raw_name as *const c_char) };
157        let raw_name = raw_name.to_str().unwrap();
158        Some(raw_name)
159    }
160
161    pub fn bind_parameter_index(&self, name: &str) -> i32 {
162        let raw_name = std::ffi::CString::new(name).unwrap();
163
164        unsafe { crate::ffi::sqlite3_bind_parameter_index(self.raw_stmt, raw_name.as_ptr()) }
165    }
166
167    pub fn bind_parameter_count(&self) -> usize {
168        unsafe { crate::ffi::sqlite3_bind_parameter_count(self.raw_stmt) as usize }
169    }
170
171    pub fn bind_parameter_name(&self, index: i32) -> Option<&str> {
172        unsafe {
173            let name = crate::ffi::sqlite3_bind_parameter_name(self.raw_stmt, index);
174            if name.is_null() {
175                None
176            } else {
177                // NOTICE: unwrap(), because SQLite promises it's valid UTF-8
178                Some(std::ffi::CStr::from_ptr(name).to_str().unwrap())
179            }
180        }
181    }
182
183    pub fn get_status(&self, status: i32) -> i32 {
184        unsafe { crate::ffi::sqlite3_stmt_status(self.raw_stmt, status, 0) }
185    }
186
187    pub fn is_explain(&self) -> i32 {
188        unsafe { crate::ffi::sqlite3_stmt_isexplain(self.raw_stmt) }
189    }
190
191    pub fn readonly(&self) -> bool {
192        unsafe { crate::ffi::sqlite3_stmt_readonly(self.raw_stmt) != 0 }
193    }
194
195    pub fn tail(&self) -> usize {
196        self.tail
197    }
198}
199
200pub unsafe fn prepare_stmt(raw: *mut crate::ffi::sqlite3, sql: &str) -> Result<Statement> {
201    let mut raw_stmt = std::ptr::null_mut();
202    let (c_sql, len) = str_for_sqlite(sql.as_bytes())?;
203    let mut c_tail: *const c_char = std::ptr::null_mut();
204
205    let err =
206        unsafe { crate::ffi::sqlite3_prepare_v2(raw, c_sql, len, &mut raw_stmt, &mut c_tail) };
207
208    // If the input text contains no SQL (if the input is an empty string or a
209    // comment) then *ppStmt is set to NULL.
210    let tail = if c_tail.is_null() {
211        0
212    } else {
213        let n = (c_tail as isize) - (c_sql as isize);
214        if n <= 0 || n >= len as isize {
215            0
216        } else {
217            n as usize
218        }
219    };
220
221    match err {
222        crate::ffi::SQLITE_OK => Ok(Statement {
223            raw_stmt,
224            tail,
225            finalized: AtomicBool::new(false),
226        }),
227        _ => Err(err.into()),
228    }
229}
230
231/// Returns `Ok((string ptr, len as c_int, SQLITE_STATIC | SQLITE_TRANSIENT))`
232/// normally.
233/// Returns error if the string is too large for sqlite.
234/// The `sqlite3_destructor_type` item is always `SQLITE_TRANSIENT` unless
235/// the string was empty (in which case it's `SQLITE_STATIC`, and the ptr is
236/// static).
237fn str_for_sqlite(s: &[u8]) -> Result<(*const c_char, c_int)> {
238    let len = len_as_c_int(s.len())?;
239    let ptr = if len != 0 {
240        s.as_ptr().cast::<c_char>()
241    } else {
242        // Return a pointer guaranteed to live forever
243        "".as_ptr().cast::<c_char>()
244    };
245    Ok((ptr, len))
246}
247
248// Helper to cast to c_int safely, returning the correct error type if the cast
249// failed.
250fn len_as_c_int(len: usize) -> Result<c_int> {
251    if len >= (c_int::MAX as usize) {
252        Err(crate::Error::from(
253            libsql_ffi::SQLITE_TOOBIG as std::ffi::c_int,
254        ))
255    } else {
256        Ok(len as c_int)
257    }
258}
259
260#[must_use]
261#[allow(non_snake_case)]
262pub fn SQLITE_TRANSIENT() -> crate::ffi::sqlite3_destructor_type {
263    Some(unsafe { std::mem::transmute(-1_isize) })
264}