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
15unsafe impl Sync for Statement {}
17
18unsafe 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 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 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
231fn 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 "".as_ptr().cast::<c_char>()
244 };
245 Ok((ptr, len))
246}
247
248fn 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}