diesel/sqlite/connection/
stmt.rs1#![allow(unsafe_code)] use super::bind_collector::{SqliteBindCollector, SqliteBindValueRef};
3use super::raw::RawConnection;
4use super::sqlite_value::OwnedSqliteValue;
5use crate::connection::Instrumentation;
6use crate::connection::statement_cache::{MaybeCached, PrepareForCache};
7use crate::query_builder::{QueryFragment, QueryId};
8use crate::result::Error::DatabaseError;
9use crate::result::*;
10use crate::sqlite::{Sqlite, SqliteType};
11use alloc::boxed::Box;
12use alloc::ffi::CString;
13use alloc::string::String;
14use alloc::vec::Vec;
15use core::cell::OnceCell;
16use core::ffi as libc;
17use core::ffi::CStr;
18use core::ptr::{self, NonNull};
19#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
20use libsqlite3_sys as ffi;
21#[cfg(all(target_family = "wasm", target_os = "unknown"))]
22use sqlite_wasm_rs as ffi;
23
24pub(super) struct Statement {
25 inner_statement: NonNull<ffi::sqlite3_stmt>,
26}
27
28#[allow(unsafe_code)]
32unsafe impl Send for Statement {}
33
34impl Statement {
35 pub(super) fn prepare(
36 raw_connection: &RawConnection,
37 sql: &str,
38 is_cached: PrepareForCache,
39 _: &[SqliteType],
40 ) -> QueryResult<Self> {
41 let mut stmt = ptr::null_mut();
42 let mut unused_portion = ptr::null();
43 let n_byte = sql
44 .len()
45 .try_into()
46 .map_err(|e| Error::SerializationError(Box::new(e)))?;
47 #[allow(clippy::unnecessary_cast)]
49 let prepare_result = unsafe {
50 ffi::sqlite3_prepare_v3(
51 raw_connection.internal_connection.as_ptr(),
52 CString::new(sql)?.as_ptr(),
53 n_byte,
54 if #[allow(non_exhaustive_omitted_patterns)] match is_cached {
PrepareForCache::Yes { counter: _ } => true,
_ => false,
}matches!(is_cached, PrepareForCache::Yes { counter: _ }) {
55 ffi::SQLITE_PREPARE_PERSISTENT as u32
56 } else {
57 0
58 },
59 &mut stmt,
60 &mut unused_portion,
61 )
62 };
63
64 ensure_sqlite_ok(prepare_result, raw_connection.internal_connection.as_ptr())?;
65
66 let inner_statement = NonNull::new(stmt).ok_or_else(|| {
69 crate::result::Error::QueryBuilderError(Box::new(crate::result::EmptyQuery))
70 })?;
71 Ok(Statement { inner_statement })
72 }
73
74 unsafe fn bind(
80 &mut self,
81 tpe: SqliteType,
82 value: SqliteBindValueRef<'_>,
83 bind_index: i32,
84 ) -> QueryResult<Option<NonNull<[u8]>>> {
85 let mut ret_ptr = None;
86 let result = match (tpe, value) {
87 (_, SqliteBindValueRef::Null) => unsafe {
88 ffi::sqlite3_bind_null(self.inner_statement.as_ptr(), bind_index)
89 },
90 (SqliteType::Binary, SqliteBindValueRef::BorrowedBinary(bytes)) => {
91 let n = bytes
92 .len()
93 .try_into()
94 .map_err(|e| Error::SerializationError(Box::new(e)))?;
95 unsafe {
96 ffi::sqlite3_bind_blob(
97 self.inner_statement.as_ptr(),
98 bind_index,
99 bytes.as_ptr() as *const libc::c_void,
100 n,
101 ffi::SQLITE_STATIC(),
102 )
103 }
104 }
105 (SqliteType::Binary, SqliteBindValueRef::Binary(mut bytes)) => {
106 let len = bytes
107 .len()
108 .try_into()
109 .map_err(|e| Error::SerializationError(Box::new(e)))?;
110 let ptr = bytes.as_mut_ptr();
114 ret_ptr = NonNull::new(Box::into_raw(bytes));
115 unsafe {
116 ffi::sqlite3_bind_blob(
117 self.inner_statement.as_ptr(),
118 bind_index,
119 ptr as *const libc::c_void,
120 len,
121 ffi::SQLITE_STATIC(),
122 )
123 }
124 }
125 (SqliteType::Text, SqliteBindValueRef::BorrowedString(bytes)) => {
126 let len = bytes
127 .len()
128 .try_into()
129 .map_err(|e| Error::SerializationError(Box::new(e)))?;
130 unsafe {
131 ffi::sqlite3_bind_text(
132 self.inner_statement.as_ptr(),
133 bind_index,
134 bytes.as_ptr() as *const libc::c_char,
135 len,
136 ffi::SQLITE_STATIC(),
137 )
138 }
139 }
140 (SqliteType::Text, SqliteBindValueRef::String(bytes)) => {
141 let mut bytes = Box::<[u8]>::from(bytes);
142 let len = bytes
143 .len()
144 .try_into()
145 .map_err(|e| Error::SerializationError(Box::new(e)))?;
146 let ptr = bytes.as_mut_ptr();
150 ret_ptr = NonNull::new(Box::into_raw(bytes));
151 unsafe {
152 ffi::sqlite3_bind_text(
153 self.inner_statement.as_ptr(),
154 bind_index,
155 ptr as *const libc::c_char,
156 len,
157 ffi::SQLITE_STATIC(),
158 )
159 }
160 }
161 (SqliteType::Float, SqliteBindValueRef::F64(value))
162 | (SqliteType::Double, SqliteBindValueRef::F64(value)) => unsafe {
163 ffi::sqlite3_bind_double(
164 self.inner_statement.as_ptr(),
165 bind_index,
166 value as libc::c_double,
167 )
168 },
169 (SqliteType::SmallInt, SqliteBindValueRef::I32(value))
170 | (SqliteType::Integer, SqliteBindValueRef::I32(value)) => unsafe {
171 ffi::sqlite3_bind_int(self.inner_statement.as_ptr(), bind_index, value)
172 },
173 (SqliteType::Long, SqliteBindValueRef::I64(value)) => unsafe {
174 ffi::sqlite3_bind_int64(self.inner_statement.as_ptr(), bind_index, value)
175 },
176 (t, b) => {
177 return Err(Error::SerializationError(
178 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Type mismatch: Expected {0:?}, got {1}",
t, b))
})alloc::format!("Type mismatch: Expected {t:?}, got {b}").into(),
179 ));
180 }
181 };
182 match ensure_sqlite_ok(result, self.raw_connection()) {
183 Ok(()) => Ok(ret_ptr),
184 Err(e) => {
185 if let Some(ptr) = ret_ptr {
186 core::mem::drop(unsafe { Box::from_raw(ptr.as_ptr()) })
190 }
191 Err(e)
192 }
193 }
194 }
195
196 fn reset(&mut self) {
197 unsafe { ffi::sqlite3_reset(self.inner_statement.as_ptr()) };
198 }
199
200 fn raw_connection(&self) -> *mut ffi::sqlite3 {
201 unsafe { ffi::sqlite3_db_handle(self.inner_statement.as_ptr()) }
202 }
203}
204
205pub(super) fn ensure_sqlite_ok(
206 code: libc::c_int,
207 raw_connection: *mut ffi::sqlite3,
208) -> QueryResult<()> {
209 if code == ffi::SQLITE_OK {
210 Ok(())
211 } else {
212 Err(last_error(raw_connection))
213 }
214}
215
216fn last_error(raw_connection: *mut ffi::sqlite3) -> Error {
217 let error_message = last_error_message(raw_connection);
218 let error_code = last_error_code(raw_connection);
219 let error_kind = match error_code {
220 ffi::SQLITE_CONSTRAINT_UNIQUE | ffi::SQLITE_CONSTRAINT_PRIMARYKEY => {
221 DatabaseErrorKind::UniqueViolation
222 }
223 ffi::SQLITE_CONSTRAINT_FOREIGNKEY => DatabaseErrorKind::ForeignKeyViolation,
224 ffi::SQLITE_CONSTRAINT_TRIGGER
228 if error_message.contains("FOREIGN KEY constraint failed") =>
229 {
230 DatabaseErrorKind::ForeignKeyViolation
231 }
232 ffi::SQLITE_CONSTRAINT_NOTNULL => DatabaseErrorKind::NotNullViolation,
233 ffi::SQLITE_CONSTRAINT_CHECK => DatabaseErrorKind::CheckViolation,
234 _ => DatabaseErrorKind::Unknown,
235 };
236 let error_information = Box::new(error_message);
237 DatabaseError(error_kind, error_information)
238}
239
240fn last_error_message(conn: *mut ffi::sqlite3) -> String {
241 let c_str = unsafe { CStr::from_ptr(ffi::sqlite3_errmsg(conn)) };
242 c_str.to_string_lossy().into_owned()
243}
244
245fn last_error_code(conn: *mut ffi::sqlite3) -> libc::c_int {
246 unsafe { ffi::sqlite3_extended_errcode(conn) }
247}
248
249impl Drop for Statement {
250 fn drop(&mut self) {
251 use crate::util::std_compat::panicking;
252
253 let raw_connection = self.raw_connection();
254 let finalize_result = unsafe { ffi::sqlite3_finalize(self.inner_statement.as_ptr()) };
255 if let Err(e) = ensure_sqlite_ok(finalize_result, raw_connection) {
256 if panicking() {
257 #[cfg(feature = "std")]
258 {
::std::io::_eprint(format_args!("Error finalizing SQLite prepared statement: {0:?}\n",
e));
};eprintln!("Error finalizing SQLite prepared statement: {e:?}");
259 } else {
260 {
::core::panicking::panic_fmt(format_args!("Error finalizing SQLite prepared statement: {0:?}",
e));
};panic!("Error finalizing SQLite prepared statement: {e:?}");
261 }
262 }
263 }
264}
265
266struct BoundStatement<'stmt, 'query> {
276 statement: MaybeCached<'stmt, Statement>,
277 query: Option<NonNull<dyn QueryFragment<Sqlite> + 'query>>,
283 binds_to_free: Vec<(i32, Option<NonNull<[u8]>>)>,
287 instrumentation: &'stmt mut dyn Instrumentation,
288 has_error: bool,
289}
290
291impl<'stmt, 'query> BoundStatement<'stmt, 'query> {
292 fn bind<T>(
293 statement: MaybeCached<'stmt, Statement>,
294 query: T,
295 instrumentation: &'stmt mut dyn Instrumentation,
296 ) -> QueryResult<BoundStatement<'stmt, 'query>>
297 where
298 T: QueryFragment<Sqlite> + QueryId + 'query,
299 {
300 let query = Box::new(query);
305
306 let mut bind_collector = SqliteBindCollector::new();
307 query.collect_binds(&mut bind_collector, &mut (), &Sqlite)?;
308 let SqliteBindCollector { binds } = bind_collector;
309
310 let mut ret = BoundStatement {
311 statement,
312 query: None,
313 binds_to_free: Vec::new(),
314 instrumentation,
315 has_error: false,
316 };
317
318 ret.bind_buffers(binds)?;
319
320 let query = query as Box<dyn QueryFragment<Sqlite> + 'query>;
321 ret.query = NonNull::new(Box::into_raw(query));
322
323 Ok(ret)
324 }
325
326 fn bind_buffers(
330 &mut self,
331 binds: Vec<(SqliteBindValueRef<'_>, SqliteType)>,
332 ) -> QueryResult<()> {
333 self.binds_to_free.reserve(
338 binds
339 .iter()
340 .filter(|&(b, _)| {
341 #[allow(non_exhaustive_omitted_patterns)] match b {
SqliteBindValueRef::BorrowedBinary(_) |
SqliteBindValueRef::BorrowedString(_) | SqliteBindValueRef::String(_)
| SqliteBindValueRef::Binary(_) => true,
_ => false,
}matches!(
342 b,
343 SqliteBindValueRef::BorrowedBinary(_)
344 | SqliteBindValueRef::BorrowedString(_)
345 | SqliteBindValueRef::String(_)
346 | SqliteBindValueRef::Binary(_)
347 )
348 })
349 .count(),
350 );
351 for (bind_idx, (bind, tpe)) in (1..).zip(binds) {
352 let is_borrowed_bind = #[allow(non_exhaustive_omitted_patterns)] match bind {
SqliteBindValueRef::BorrowedString(_) |
SqliteBindValueRef::BorrowedBinary(_) => true,
_ => false,
}matches!(
353 bind,
354 SqliteBindValueRef::BorrowedString(_) | SqliteBindValueRef::BorrowedBinary(_)
355 );
356
357 let res = unsafe { self.statement.bind(tpe, bind, bind_idx) }?;
362
363 if let Some(ptr) = res {
368 self.binds_to_free.push((bind_idx, Some(ptr)));
371 } else if is_borrowed_bind {
372 self.binds_to_free.push((bind_idx, None));
374 }
375 }
376 Ok(())
377 }
378
379 fn finish_query_with_error(mut self, e: &Error) {
380 self.has_error = true;
381 if let Some(q) = self.query {
382 let q = unsafe { q.as_ref() };
384 self.instrumentation.on_connection_event(
385 crate::connection::InstrumentationEvent::FinishQuery {
386 query: &crate::debug_query(&q),
387 error: Some(e),
388 },
389 );
390 }
391 }
392}
393
394impl Drop for BoundStatement<'_, '_> {
395 fn drop(&mut self) {
396 self.statement.reset();
399
400 for (idx, buffer) in core::mem::take(&mut self.binds_to_free) {
401 unsafe {
402 self.statement
404 .bind(SqliteType::Text, SqliteBindValueRef::Null, idx)
405 .expect(
406 "Binding a null value should never fail. \
407 If you ever see this error message please open \
408 an issue at diesels issue tracker containing \
409 code how to trigger this message.",
410 );
411 }
412
413 if let Some(buffer) = buffer {
414 unsafe {
415 core::mem::drop(Box::from_raw(buffer.as_ptr()));
418 }
419 }
420 }
421
422 if let Some(query) = self.query {
423 let query = unsafe {
424 Box::from_raw(query.as_ptr())
427 };
428 if !self.has_error {
429 self.instrumentation.on_connection_event(
430 crate::connection::InstrumentationEvent::FinishQuery {
431 query: &crate::debug_query(&query),
432 error: None,
433 },
434 );
435 }
436 core::mem::drop(query);
437 self.query = None;
438 }
439 }
440}
441
442#[allow(missing_debug_implementations)]
443pub struct StatementUse<'stmt, 'query> {
444 statement: BoundStatement<'stmt, 'query>,
445 column_names: OnceCell<Vec<*const str>>,
467}
468
469impl<'stmt, 'query> StatementUse<'stmt, 'query> {
470 pub(super) fn bind<T>(
471 statement: MaybeCached<'stmt, Statement>,
472 query: T,
473 instrumentation: &'stmt mut dyn Instrumentation,
474 ) -> QueryResult<StatementUse<'stmt, 'query>>
475 where
476 T: QueryFragment<Sqlite> + QueryId + 'query,
477 {
478 Ok(Self {
479 statement: BoundStatement::bind(statement, query, instrumentation)?,
480 column_names: OnceCell::new(),
481 })
482 }
483
484 pub(super) fn run(mut self) -> QueryResult<()> {
485 let r = unsafe {
486 self.step(true).map(|_| ())
490 };
491 if let Err(ref e) = r {
492 self.statement.finish_query_with_error(e);
493 }
494 r
495 }
496
497 pub(super) unsafe fn step(&mut self, first_step: bool) -> QueryResult<bool> {
504 let step_result =
505 unsafe { ffi::sqlite3_step(self.statement.statement.inner_statement.as_ptr()) };
506 let res = match step_result {
507 ffi::SQLITE_DONE => Ok(false),
508 ffi::SQLITE_ROW => Ok(true),
509 _ => Err(last_error(self.statement.statement.raw_connection())),
510 };
511 if first_step {
512 self.column_names = OnceCell::new();
513 }
514 res
515 }
516
517 unsafe fn column_name(&self, idx: i32) -> *const str {
529 let name = {
530 let column_name = unsafe {
531 ffi::sqlite3_column_name(self.statement.statement.inner_statement.as_ptr(), idx)
532 };
533 if !!column_name.is_null() {
{
::core::panicking::panic_fmt(format_args!("The Sqlite documentation states that it only returns a null pointer here if we are in a OOM condition."));
}
};assert!(
534 !column_name.is_null(),
535 "The Sqlite documentation states that it only returns a \
536 null pointer here if we are in a OOM condition."
537 );
538 unsafe { CStr::from_ptr(column_name) }
539 };
540 name.to_str().expect(
541 "The Sqlite documentation states that this is UTF8. \
542 If you see this error message something has gone \
543 horribly wrong. Please open an issue at the \
544 diesel repository.",
545 ) as *const str
546 }
547
548 pub(super) fn column_count(&self) -> i32 {
549 unsafe { ffi::sqlite3_column_count(self.statement.statement.inner_statement.as_ptr()) }
550 }
551
552 pub(super) fn index_for_column_name(&mut self, field_name: &str) -> Option<usize> {
553 (0..self.column_count())
554 .find(|idx| self.field_name(*idx) == Some(field_name))
555 .map(|v| {
556 v.try_into()
557 .expect("Diesel expects to run at least on a 32 bit platform")
558 })
559 }
560
561 pub(super) fn field_name(&self, idx: i32) -> Option<&str> {
562 let column_names = self.column_names.get_or_init(|| {
563 let count = self.column_count();
564 (0..count)
565 .map(|idx| unsafe {
566 self.column_name(idx)
569 })
570 .collect()
571 });
572
573 column_names
574 .get(usize::try_from(idx).expect("Diesel expects to run at least on a 32 bit platform"))
575 .and_then(|c| unsafe { c.as_ref() })
576 }
577
578 pub(super) fn copy_value(&self, idx: i32) -> Option<OwnedSqliteValue> {
579 OwnedSqliteValue::copy_from_ptr(self.column_value(idx)?)
580 }
581
582 pub(super) fn column_value(&self, idx: i32) -> Option<NonNull<ffi::sqlite3_value>> {
583 let ptr = unsafe {
584 ffi::sqlite3_column_value(self.statement.statement.inner_statement.as_ptr(), idx)
585 };
586 NonNull::new(ptr)
587 }
588}
589
590#[cfg(test)]
591mod tests {
592 use crate::prelude::*;
593 use crate::sql_types::Text;
594
595 #[diesel_test_helper::test]
598 fn check_out_of_bounds_bind_does_not_panic_on_drop() {
599 let mut conn = SqliteConnection::establish(":memory:").unwrap();
600
601 let e = crate::sql_query("SELECT '?'")
602 .bind::<Text, _>("foo")
603 .execute(&mut conn);
604
605 assert!(e.is_err());
606 let e = e.unwrap_err();
607 if let crate::result::Error::DatabaseError(crate::result::DatabaseErrorKind::Unknown, m) = e
608 {
609 assert_eq!(m.message(), "column index out of range");
610 } else {
611 panic!("Wrong error returned");
612 }
613 }
614}