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, PreparedStatement};
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 PreparedStatement for Statement {
35 fn parameter_count(&self) -> u64 {
36 unsafe {
38 ffi::sqlite3_bind_parameter_count(self.inner_statement.as_ptr())
39 .try_into()
40 .unwrap_or(0)
41 }
42 }
43}
44
45impl Statement {
46 pub(super) fn prepare(
47 raw_connection: &RawConnection,
48 sql: &str,
49 is_cached: PrepareForCache,
50 _: &[SqliteType],
51 ) -> QueryResult<Self> {
52 let mut stmt = ptr::null_mut();
53 let mut unused_portion = ptr::null();
54 let n_byte = sql
55 .len()
56 .try_into()
57 .map_err(|e| Error::SerializationError(Box::new(e)))?;
58 #[allow(clippy::unnecessary_cast)]
60 let prepare_result = unsafe {
61 ffi::sqlite3_prepare_v3(
62 raw_connection.internal_connection.as_ptr(),
63 CString::new(sql)?.as_ptr(),
64 n_byte,
65 if #[allow(non_exhaustive_omitted_patterns)] match is_cached {
PrepareForCache::Yes { counter: _ } => true,
_ => false,
}matches!(is_cached, PrepareForCache::Yes { counter: _ }) {
66 ffi::SQLITE_PREPARE_PERSISTENT as u32
67 } else {
68 0
69 },
70 &mut stmt,
71 &mut unused_portion,
72 )
73 };
74
75 ensure_sqlite_ok(prepare_result, raw_connection.internal_connection.as_ptr())?;
76
77 let inner_statement = NonNull::new(stmt).ok_or_else(|| {
80 crate::result::Error::QueryBuilderError(Box::new(crate::result::EmptyQuery))
81 })?;
82 Ok(Statement { inner_statement })
83 }
84
85 unsafe fn bind(
91 &mut self,
92 tpe: SqliteType,
93 value: SqliteBindValueRef<'_>,
94 bind_index: i32,
95 ) -> QueryResult<Option<NonNull<[u8]>>> {
96 let mut ret_ptr = None;
97 let result = match (tpe, value) {
98 (_, SqliteBindValueRef::Null) => unsafe {
99 ffi::sqlite3_bind_null(self.inner_statement.as_ptr(), bind_index)
100 },
101 (SqliteType::Binary, SqliteBindValueRef::BorrowedBinary(bytes)) => {
102 let n = bytes
103 .len()
104 .try_into()
105 .map_err(|e| Error::SerializationError(Box::new(e)))?;
106 unsafe {
107 ffi::sqlite3_bind_blob(
108 self.inner_statement.as_ptr(),
109 bind_index,
110 bytes.as_ptr() as *const libc::c_void,
111 n,
112 ffi::SQLITE_STATIC(),
113 )
114 }
115 }
116 (SqliteType::Binary, SqliteBindValueRef::Binary(mut bytes)) => {
117 let len = bytes
118 .len()
119 .try_into()
120 .map_err(|e| Error::SerializationError(Box::new(e)))?;
121 let ptr = bytes.as_mut_ptr();
125 ret_ptr = NonNull::new(Box::into_raw(bytes));
126 unsafe {
127 ffi::sqlite3_bind_blob(
128 self.inner_statement.as_ptr(),
129 bind_index,
130 ptr as *const libc::c_void,
131 len,
132 ffi::SQLITE_STATIC(),
133 )
134 }
135 }
136 (SqliteType::Text, SqliteBindValueRef::BorrowedString(bytes)) => {
137 let len = bytes
138 .len()
139 .try_into()
140 .map_err(|e| Error::SerializationError(Box::new(e)))?;
141 unsafe {
142 ffi::sqlite3_bind_text(
143 self.inner_statement.as_ptr(),
144 bind_index,
145 bytes.as_ptr() as *const libc::c_char,
146 len,
147 ffi::SQLITE_STATIC(),
148 )
149 }
150 }
151 (SqliteType::Text, SqliteBindValueRef::String(bytes)) => {
152 let mut bytes = Box::<[u8]>::from(bytes);
153 let len = bytes
154 .len()
155 .try_into()
156 .map_err(|e| Error::SerializationError(Box::new(e)))?;
157 let ptr = bytes.as_mut_ptr();
161 ret_ptr = NonNull::new(Box::into_raw(bytes));
162 unsafe {
163 ffi::sqlite3_bind_text(
164 self.inner_statement.as_ptr(),
165 bind_index,
166 ptr as *const libc::c_char,
167 len,
168 ffi::SQLITE_STATIC(),
169 )
170 }
171 }
172 (SqliteType::Float, SqliteBindValueRef::F64(value))
173 | (SqliteType::Double, SqliteBindValueRef::F64(value)) => unsafe {
174 ffi::sqlite3_bind_double(
175 self.inner_statement.as_ptr(),
176 bind_index,
177 value as libc::c_double,
178 )
179 },
180 (SqliteType::SmallInt, SqliteBindValueRef::I32(value))
181 | (SqliteType::Integer, SqliteBindValueRef::I32(value)) => unsafe {
182 ffi::sqlite3_bind_int(self.inner_statement.as_ptr(), bind_index, value)
183 },
184 (SqliteType::Long, SqliteBindValueRef::I64(value)) => unsafe {
185 ffi::sqlite3_bind_int64(self.inner_statement.as_ptr(), bind_index, value)
186 },
187 (t, b) => {
188 return Err(Error::SerializationError(
189 ::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(),
190 ));
191 }
192 };
193 match ensure_sqlite_ok(result, self.raw_connection()) {
194 Ok(()) => Ok(ret_ptr),
195 Err(e) => {
196 if let Some(ptr) = ret_ptr {
197 core::mem::drop(unsafe { Box::from_raw(ptr.as_ptr()) })
201 }
202 Err(e)
203 }
204 }
205 }
206
207 fn reset(&mut self) {
208 unsafe { ffi::sqlite3_reset(self.inner_statement.as_ptr()) };
209 }
210
211 #[cfg(not(miri))]
213 pub(super) fn run(&mut self) -> QueryResult<()> {
214 match unsafe { ffi::sqlite3_step(self.inner_statement.as_ptr()) } {
216 ffi::SQLITE_DONE | ffi::SQLITE_ROW => Ok(()),
217 _ => {
218 let error = last_error(self.raw_connection());
219 self.reset();
221 Err(error)
222 }
223 }
224 }
225
226 fn clear_bindings(&mut self) {
227 unsafe { ffi::sqlite3_clear_bindings(self.inner_statement.as_ptr()) };
228 }
229
230 fn raw_connection(&self) -> *mut ffi::sqlite3 {
231 unsafe { ffi::sqlite3_db_handle(self.inner_statement.as_ptr()) }
232 }
233}
234
235pub(super) fn ensure_sqlite_ok(
236 code: libc::c_int,
237 raw_connection: *mut ffi::sqlite3,
238) -> QueryResult<()> {
239 if code == ffi::SQLITE_OK {
240 Ok(())
241 } else {
242 Err(last_error(raw_connection))
243 }
244}
245
246fn last_error(raw_connection: *mut ffi::sqlite3) -> Error {
247 let error_message = last_error_message(raw_connection);
248 let error_code = last_error_code(raw_connection);
249 let error_kind = match error_code {
250 ffi::SQLITE_CONSTRAINT_UNIQUE | ffi::SQLITE_CONSTRAINT_PRIMARYKEY => {
251 DatabaseErrorKind::UniqueViolation
252 }
253 ffi::SQLITE_CONSTRAINT_FOREIGNKEY => DatabaseErrorKind::ForeignKeyViolation,
254 ffi::SQLITE_CONSTRAINT_TRIGGER
258 if error_message.contains("FOREIGN KEY constraint failed") =>
259 {
260 DatabaseErrorKind::ForeignKeyViolation
261 }
262 ffi::SQLITE_CONSTRAINT_NOTNULL => DatabaseErrorKind::NotNullViolation,
263 ffi::SQLITE_CONSTRAINT_CHECK => DatabaseErrorKind::CheckViolation,
264 _ => DatabaseErrorKind::Unknown,
265 };
266 let error_information = Box::new(error_message);
267 DatabaseError(error_kind, error_information)
268}
269
270fn last_error_message(conn: *mut ffi::sqlite3) -> String {
271 let c_str = unsafe { CStr::from_ptr(ffi::sqlite3_errmsg(conn)) };
272 c_str.to_string_lossy().into_owned()
273}
274
275fn last_error_code(conn: *mut ffi::sqlite3) -> libc::c_int {
276 unsafe { ffi::sqlite3_extended_errcode(conn) }
277}
278
279impl Drop for Statement {
280 fn drop(&mut self) {
281 unsafe { ffi::sqlite3_finalize(self.inner_statement.as_ptr()) };
283 }
284}
285
286struct BoundStatement<'stmt, 'query> {
296 statement: MaybeCached<'stmt, Statement>,
297 query: Option<NonNull<dyn QueryFragment<Sqlite> + 'query>>,
303 binds_to_free: Vec<(i32, Option<NonNull<[u8]>>)>,
307 instrumentation: &'stmt mut dyn Instrumentation,
308 has_error: bool,
309}
310
311impl<'stmt, 'query> BoundStatement<'stmt, 'query> {
312 fn bind<T>(
313 mut statement: MaybeCached<'stmt, Statement>,
314 query: T,
315 instrumentation: &'stmt mut dyn Instrumentation,
316 ) -> QueryResult<BoundStatement<'stmt, 'query>>
317 where
318 T: QueryFragment<Sqlite> + QueryId + 'query,
319 {
320 let query = Box::new(query);
325 statement.reset();
327 statement.clear_bindings();
328
329 let mut bind_collector = SqliteBindCollector::new();
330 query.collect_binds(&mut bind_collector, &mut (), &Sqlite)?;
331
332 let SqliteBindCollector { binds } = bind_collector;
333 if statement.parameter_count() != binds.len() as u64 {
334 return Err(
335 crate::connection::statement_cache::invalid_prepared_statement_parameter_count(
336 statement.parameter_count().try_into().unwrap_or(usize::MAX),
337 binds.len(),
338 ),
339 );
340 }
341
342 let mut ret = BoundStatement {
343 statement,
344 query: None,
345 binds_to_free: Vec::new(),
346 instrumentation,
347 has_error: false,
348 };
349
350 ret.bind_buffers(binds)?;
351
352 let query = query as Box<dyn QueryFragment<Sqlite> + 'query>;
353 ret.query = NonNull::new(Box::into_raw(query));
354
355 Ok(ret)
356 }
357
358 fn bind_buffers(
362 &mut self,
363 binds: Vec<(SqliteBindValueRef<'_>, SqliteType)>,
364 ) -> QueryResult<()> {
365 self.binds_to_free.reserve(
370 binds
371 .iter()
372 .filter(|&(b, _)| {
373 #[allow(non_exhaustive_omitted_patterns)] match b {
SqliteBindValueRef::BorrowedBinary(_) |
SqliteBindValueRef::BorrowedString(_) | SqliteBindValueRef::String(_)
| SqliteBindValueRef::Binary(_) => true,
_ => false,
}matches!(
374 b,
375 SqliteBindValueRef::BorrowedBinary(_)
376 | SqliteBindValueRef::BorrowedString(_)
377 | SqliteBindValueRef::String(_)
378 | SqliteBindValueRef::Binary(_)
379 )
380 })
381 .count(),
382 );
383 for (bind_idx, (bind, tpe)) in (1..).zip(binds) {
384 let is_borrowed_bind = #[allow(non_exhaustive_omitted_patterns)] match bind {
SqliteBindValueRef::BorrowedString(_) |
SqliteBindValueRef::BorrowedBinary(_) => true,
_ => false,
}matches!(
385 bind,
386 SqliteBindValueRef::BorrowedString(_) | SqliteBindValueRef::BorrowedBinary(_)
387 );
388
389 let res = unsafe { self.statement.bind(tpe, bind, bind_idx) }?;
394
395 if let Some(ptr) = res {
400 self.binds_to_free.push((bind_idx, Some(ptr)));
403 } else if is_borrowed_bind {
404 self.binds_to_free.push((bind_idx, None));
406 }
407 }
408 Ok(())
409 }
410
411 fn finish_query_with_error(mut self, e: &Error) {
412 self.has_error = true;
413 if let Some(q) = self.query {
414 let q = unsafe { q.as_ref() };
416 self.instrumentation.on_connection_event(
417 crate::connection::InstrumentationEvent::FinishQuery {
418 query: &crate::debug_query(&q),
419 error: Some(e),
420 },
421 );
422 }
423 }
424}
425
426impl Drop for BoundStatement<'_, '_> {
427 fn drop(&mut self) {
428 self.statement.reset();
431
432 for (idx, buffer) in core::mem::take(&mut self.binds_to_free) {
433 unsafe {
434 self.statement
436 .bind(SqliteType::Text, SqliteBindValueRef::Null, idx)
437 .expect(
438 "Binding a null value should never fail. \
439 If you ever see this error message please open \
440 an issue at diesels issue tracker containing \
441 code how to trigger this message.",
442 );
443 }
444
445 if let Some(buffer) = buffer {
446 unsafe {
447 core::mem::drop(Box::from_raw(buffer.as_ptr()));
450 }
451 }
452 }
453
454 if let Some(query) = self.query {
455 let query = unsafe {
456 Box::from_raw(query.as_ptr())
459 };
460 if !self.has_error {
461 self.instrumentation.on_connection_event(
462 crate::connection::InstrumentationEvent::FinishQuery {
463 query: &crate::debug_query(&query),
464 error: None,
465 },
466 );
467 }
468 core::mem::drop(query);
469 self.query = None;
470 }
471 }
472}
473
474#[allow(missing_debug_implementations)]
475pub struct StatementUse<'stmt, 'query> {
476 statement: BoundStatement<'stmt, 'query>,
477 column_names: OnceCell<Vec<*const str>>,
499}
500
501impl<'stmt, 'query> StatementUse<'stmt, 'query> {
502 pub(super) fn bind<T>(
503 statement: MaybeCached<'stmt, Statement>,
504 query: T,
505 instrumentation: &'stmt mut dyn Instrumentation,
506 ) -> QueryResult<StatementUse<'stmt, 'query>>
507 where
508 T: QueryFragment<Sqlite> + QueryId + 'query,
509 {
510 Ok(Self {
511 statement: BoundStatement::bind(statement, query, instrumentation)?,
512 column_names: OnceCell::new(),
513 })
514 }
515
516 pub(super) fn run(mut self) -> QueryResult<()> {
517 let r = unsafe {
518 self.step(true).map(|_| ())
522 };
523 if let Err(ref e) = r {
524 self.statement.finish_query_with_error(e);
525 }
526 r
527 }
528
529 pub(super) unsafe fn step(&mut self, first_step: bool) -> QueryResult<bool> {
536 let step_result =
537 unsafe { ffi::sqlite3_step(self.statement.statement.inner_statement.as_ptr()) };
538 let res = match step_result {
539 ffi::SQLITE_DONE => Ok(false),
540 ffi::SQLITE_ROW => Ok(true),
541 _ => Err(last_error(self.statement.statement.raw_connection())),
542 };
543 if first_step {
544 self.column_names = OnceCell::new();
545 }
546 res
547 }
548
549 unsafe fn column_name(&self, idx: i32) -> *const str {
561 let name = {
562 let column_name = unsafe {
563 ffi::sqlite3_column_name(self.statement.statement.inner_statement.as_ptr(), idx)
564 };
565 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!(
566 !column_name.is_null(),
567 "The Sqlite documentation states that it only returns a \
568 null pointer here if we are in a OOM condition."
569 );
570 unsafe { CStr::from_ptr(column_name) }
571 };
572 name.to_str().expect(
573 "The Sqlite documentation states that this is UTF8. \
574 If you see this error message something has gone \
575 horribly wrong. Please open an issue at the \
576 diesel repository.",
577 ) as *const str
578 }
579
580 pub(super) fn column_count(&self) -> i32 {
581 unsafe { ffi::sqlite3_column_count(self.statement.statement.inner_statement.as_ptr()) }
582 }
583
584 pub(super) fn index_for_column_name(&mut self, field_name: &str) -> Option<usize> {
585 (0..self.column_count())
586 .find(|idx| self.field_name(*idx) == Some(field_name))
587 .map(|v| {
588 v.try_into()
589 .expect("Diesel expects to run at least on a 32 bit platform")
590 })
591 }
592
593 pub(super) fn field_name(&self, idx: i32) -> Option<&str> {
594 let column_names = self.column_names.get_or_init(|| {
595 let count = self.column_count();
596 (0..count)
597 .map(|idx| unsafe {
598 self.column_name(idx)
601 })
602 .collect()
603 });
604
605 column_names
606 .get(usize::try_from(idx).expect("Diesel expects to run at least on a 32 bit platform"))
607 .and_then(|c| unsafe { c.as_ref() })
608 }
609
610 pub(super) fn copy_value(&self, idx: i32) -> QueryResult<Option<OwnedSqliteValue>> {
611 match self.column_value(idx) {
612 Some(ptr) => OwnedSqliteValue::copy_from_ptr(ptr),
613 None => Ok(None),
614 }
615 }
616
617 pub(super) fn column_value(&self, idx: i32) -> Option<NonNull<ffi::sqlite3_value>> {
618 let ptr = unsafe {
619 ffi::sqlite3_column_value(self.statement.statement.inner_statement.as_ptr(), idx)
620 };
621 NonNull::new(ptr)
622 }
623
624 pub(super) fn raw_connection(&self) -> *mut ffi::sqlite3 {
625 self.statement.statement.raw_connection()
626 }
627}
628
629#[cfg(test)]
630mod tests {
631 #[cfg(not(miri))]
632 use crate::prelude::*;
633 #[cfg(not(miri))]
634 use crate::sql_types::Text;
635
636 #[cfg(not(miri))] #[diesel_test_helper::test]
644 fn check_out_of_bounds_bind_does_not_panic_on_drop() {
645 let mut conn = SqliteConnection::establish(":memory:").unwrap();
646
647 let e = crate::sql_query("SELECT '?'")
648 .bind::<Text, _>("foo")
649 .execute(&mut conn);
650
651 assert!(e.is_err());
652 let e = e.unwrap_err();
653 assert_eq!(
654 e.to_string(),
655 "prepared statement expected a different number of parameters than provided: Expected 0, provided: 1"
656 );
657 }
658}