1#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2extern crate libsqlite3_sys as ffi;
3
4#[cfg(all(target_family = "wasm", target_os = "unknown"))]
5use sqlite_wasm_rs as ffi;
6
7pub mod authorizer;
8mod bind_collector;
9mod collation_needed;
10mod functions;
11mod hooks;
12mod limits;
13mod owned_row;
14mod raw;
15mod row;
16mod serialized_database;
17pub(in crate::sqlite) mod sqlite_blob;
18mod sqlite_value;
19mod statement_iterator;
20mod stmt;
21mod trace;
22mod update_hook;
23
24pub use self::authorizer::{AuthorizerContext, AuthorizerDecision};
25pub use self::bind_collector::SqliteBindCollector;#[diesel_derives::__diesel_public_if(
26 feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
27)]
28pub(in crate::sqlite) use self::bind_collector::SqliteBindCollector;
29pub use self::bind_collector::SqliteBindValue;
30#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
31pub use self::bind_collector::{OwnedSqliteBindValue, SqliteBindCollectorData, SqliteBindValueRef};
32pub use self::collation_needed::{CollationNeededContext, SqliteTextRep};
33pub use self::limits::SqliteLimit;
34use self::raw::RawConnection;
35pub use self::serialized_database::SerializedDatabase;
36pub use self::sqlite_value::SqliteValue;
37use self::statement_iterator::*;
38use self::stmt::{Statement, StatementUse};
39pub use self::trace::{SqliteTraceEvent, SqliteTraceFlags};
40pub use self::update_hook::{
41 SqliteChangeEvent, SqliteChangeOp, SqliteChangeOps, SqliteUpdateRouter,
42};
43use super::SqliteAggregateFunction;
44use crate::connection::instrumentation::{DynInstrumentation, StrQueryHelper};
45use crate::connection::statement_cache::StatementCache;
46use crate::connection::*;
47use crate::deserialize::{FromSqlRow, StaticallySizedRow};
48use crate::expression::QueryMetadata;
49use crate::query_builder::*;
50use crate::query_dsl::RunQueryDslSupport;
51use crate::query_source::{ColumnHasTable, NamedTable};
52use crate::result::*;
53use crate::serialize::ToSql;
54use crate::sql_types::{HasSqlType, TypeMetadata};
55use crate::sqlite::{Sqlite, SqliteFunctionBehavior};
56use alloc::string::String;
57use alloc::string::ToString;
58use alloc::vec::Vec;
59use core::ffi as libc;
60use core::marker::PhantomData;
61use core::num::NonZeroI64;
62
63#[allow(missing_debug_implementations)]
186#[cfg(feature = "__sqlite-shared")]
187pub struct SqliteConnection {
188 statement_cache: StatementCache<Sqlite, Statement>,
192 raw_connection: RawConnection,
193 transaction_state: AnsiTransactionManager,
194 metadata_lookup: (),
197 instrumentation: DynInstrumentation,
198 serialized_data: Vec<Vec<u8>>,
209}
210
211#[allow(unsafe_code)]
215unsafe impl Send for SqliteConnection {}
216
217impl SimpleConnection for SqliteConnection {
218 fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
219 self.instrumentation
220 .on_connection_event(InstrumentationEvent::StartQuery {
221 query: &StrQueryHelper::new(query),
222 });
223 let resp = self.raw_connection.exec(query);
224 self.instrumentation
225 .on_connection_event(InstrumentationEvent::FinishQuery {
226 query: &StrQueryHelper::new(query),
227 error: resp.as_ref().err(),
228 });
229 resp
230 }
231}
232
233impl ConnectionSealed for SqliteConnection {}
234
235impl Connection for SqliteConnection {
236 type Backend = Sqlite;
237 type TransactionManager = AnsiTransactionManager;
238
239 fn establish(database_url: &str) -> ConnectionResult<Self> {
255 let mut instrumentation = DynInstrumentation::default_instrumentation();
256 instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
257 url: database_url,
258 });
259
260 let establish_result = Self::establish_inner(database_url);
261 instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
262 url: database_url,
263 error: establish_result.as_ref().err(),
264 });
265 let mut conn = establish_result?;
266 conn.instrumentation = instrumentation;
267 Ok(conn)
268 }
269
270 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
271 where
272 T: QueryFragment<Self::Backend> + QueryId,
273 {
274 let statement_use = self.prepared_query(source)?;
275 statement_use.run().and_then(|_| {
276 self.raw_connection
277 .rows_affected_by_last_query()
278 .map_err(Error::DeserializationError)
279 })
280 }
281
282 fn transaction_state(&mut self) -> &mut AnsiTransactionManager
283 where
284 Self: Sized,
285 {
286 &mut self.transaction_state
287 }
288
289 fn instrumentation(&mut self) -> &mut dyn Instrumentation {
290 &mut *self.instrumentation
291 }
292
293 fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
294 self.instrumentation = instrumentation.into();
295 }
296
297 fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
298 self.statement_cache.set_cache_size(size);
299 }
300}
301
302impl LoadConnection<DefaultLoadingMode> for SqliteConnection {
303 type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>;
304 type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>;
305
306 fn load<'conn, 'query, T>(
307 &'conn mut self,
308 source: T,
309 ) -> QueryResult<Self::Cursor<'conn, 'query>>
310 where
311 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
312 Self::Backend: QueryMetadata<T::SqlType>,
313 {
314 let statement = self.prepared_query(source)?;
315
316 Ok(StatementIterator::new(statement))
317 }
318}
319
320impl WithMetadataLookup for SqliteConnection {
321 fn metadata_lookup(&mut self) -> &mut <Sqlite as TypeMetadata>::MetadataLookup {
322 &mut self.metadata_lookup
323 }
324}
325
326#[cfg(feature = "r2d2")]
327impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection {
328 fn ping(&mut self) -> QueryResult<()> {
329 use crate::RunQueryDsl;
330
331 crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
332 }
333
334 fn is_broken(&mut self) -> bool {
335 AnsiTransactionManager::is_broken_transaction_manager(self)
336 }
337}
338
339impl MultiConnectionHelper for SqliteConnection {
340 fn to_any<'a>(
341 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
342 ) -> &mut (dyn core::any::Any + 'a) {
343 lookup
344 }
345
346 fn from_any(
347 lookup: &mut dyn core::any::Any,
348 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
349 lookup.downcast_mut()
350 }
351}
352
353#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CommitDecision {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
CommitDecision::Proceed => "Proceed",
CommitDecision::Rollback => "Rollback",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for CommitDecision {
#[inline]
fn clone(&self) -> CommitDecision { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CommitDecision { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for CommitDecision {
#[inline]
fn eq(&self, other: &CommitDecision) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CommitDecision {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
356pub enum CommitDecision {
357 Proceed,
359 Rollback,
361}
362
363#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ProgressDecision {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
ProgressDecision::Continue => "Continue",
ProgressDecision::Interrupt => "Interrupt",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for ProgressDecision {
#[inline]
fn clone(&self) -> ProgressDecision { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ProgressDecision { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for ProgressDecision {
#[inline]
fn eq(&self, other: &ProgressDecision) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for ProgressDecision {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
366pub enum ProgressDecision {
367 Continue,
369 Interrupt,
371}
372
373#[derive(#[automatically_derived]
impl ::core::fmt::Debug for BusyDecision {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
BusyDecision::Retry => "Retry",
BusyDecision::GiveUp => "GiveUp",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for BusyDecision {
#[inline]
fn clone(&self) -> BusyDecision { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BusyDecision { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for BusyDecision {
#[inline]
fn eq(&self, other: &BusyDecision) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for BusyDecision {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
376pub enum BusyDecision {
377 Retry,
379 GiveUp,
381}
382
383#[derive(#[automatically_derived]
impl ::core::fmt::Debug for AutoVacuumMode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
AutoVacuumMode::None => "None",
AutoVacuumMode::Full => "Full",
AutoVacuumMode::Incremental => "Incremental",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for AutoVacuumMode {
#[inline]
fn clone(&self) -> AutoVacuumMode { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for AutoVacuumMode { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for AutoVacuumMode {
#[inline]
fn eq(&self, other: &AutoVacuumMode) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for AutoVacuumMode {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq, const _: () =
{
use diesel;
impl<__DB>
diesel::deserialize::FromSql<crate::sql_types::Integer, __DB> for
AutoVacuumMode where __DB: diesel::backend::Backend,
crate::sql_types::Integer: diesel::sql_types::EnumSqlType<true,
__DB>,
<crate::sql_types::Integer as
diesel::sql_types::EnumSqlType<true,
__DB>>::Strategy: diesel::internal::derives::enum_::EnumMapping<__DB>
{
fn from_sql(value:
<__DB as diesel::backend::Backend>::RawValue<'_>)
-> diesel::deserialize::Result<Self> {
const VARIANTS:
&[diesel::internal::derives::enum_::EnumVariant] =
&[diesel::internal::derives::enum_::EnumVariant {
discriminant: 0i128,
rust_name: "None",
sql_name: "None",
},
diesel::internal::derives::enum_::EnumVariant {
discriminant: 1i128,
rust_name: "Full",
sql_name: "Full",
},
diesel::internal::derives::enum_::EnumVariant {
discriminant: 2i128,
rust_name: "Incremental",
sql_name: "Incremental",
}];
let idx =
<<crate::sql_types::Integer as
diesel::sql_types::EnumSqlType<true, __DB>>::Strategy as
diesel::internal::derives::enum_::EnumMapping<__DB>>::map_from_database_value(value,
"AutoVacuumMode", VARIANTS)?;
match idx {
0usize => Ok(Self::None),
1usize => Ok(Self::Full),
2usize => Ok(Self::Incremental),
_ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("We construct all relevant variants")));
}
}
}
}
impl<__DB> diesel::serialize::ToSql<crate::sql_types::Integer, __DB>
for AutoVacuumMode where __DB: diesel::backend::Backend,
crate::sql_types::Integer: diesel::sql_types::EnumSqlType<true,
__DB>,
<crate::sql_types::Integer as
diesel::sql_types::EnumSqlType<true,
__DB>>::Strategy: diesel::internal::derives::enum_::EnumMapping<__DB>
{
fn to_sql<'b>(&'b self,
output: &mut diesel::serialize::Output<'b, '_, __DB>)
-> diesel::serialize::Result {
let variant =
match self {
Self::None =>
&diesel::internal::derives::enum_::EnumVariant {
discriminant: 0i128,
rust_name: "None",
sql_name: "None",
},
Self::Full =>
&diesel::internal::derives::enum_::EnumVariant {
discriminant: 1i128,
rust_name: "Full",
sql_name: "Full",
},
Self::Incremental =>
&diesel::internal::derives::enum_::EnumVariant {
discriminant: 2i128,
rust_name: "Incremental",
sql_name: "Incremental",
},
};
<<crate::sql_types::Integer as
diesel::sql_types::EnumSqlType<true, __DB>>::Strategy as
diesel::internal::derives::enum_::EnumMapping<__DB>>::map_to_database_value(output,
variant)
}
}
impl<'__expr>
diesel::expression::AsExpression<crate::sql_types::Integer> for
&'__expr AutoVacuumMode {
type Expression =
diesel::internal::derives::as_expression::Bound<crate::sql_types::Integer,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<crate::sql_types::Integer>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr>
diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>
for &'__expr AutoVacuumMode {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<crate::sql_types::Integer>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, '__expr2>
diesel::expression::AsExpression<crate::sql_types::Integer> for
&'__expr2 &'__expr AutoVacuumMode {
type Expression =
diesel::internal::derives::as_expression::Bound<crate::sql_types::Integer,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<crate::sql_types::Integer>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
#[diagnostic::do_not_recommend]
impl<'__expr, '__expr2>
diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>
for &'__expr2 &'__expr AutoVacuumMode {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<crate::sql_types::Integer>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl<__DB>
diesel::serialize::ToSql<diesel::sql_types::Nullable<crate::sql_types::Integer>,
__DB> for AutoVacuumMode where __DB: diesel::backend::Backend,
Self: diesel::serialize::ToSql<crate::sql_types::Integer, __DB> {
fn to_sql<'__b>(&'__b self,
out: &mut diesel::serialize::Output<'__b, '_, __DB>)
-> diesel::serialize::Result {
diesel::serialize::ToSql::<crate::sql_types::Integer,
__DB>::to_sql(self, out)
}
}
impl diesel::expression::AsExpression<crate::sql_types::Integer> for
AutoVacuumMode {
type Expression =
diesel::internal::derives::as_expression::Bound<crate::sql_types::Integer,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<crate::sql_types::Integer>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>
for AutoVacuumMode {
type Expression =
diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<crate::sql_types::Integer>,
Self>;
fn as_expression(self)
->
<Self as
diesel::expression::AsExpression<diesel::sql_types::Nullable<crate::sql_types::Integer>>>::Expression {
diesel::internal::derives::as_expression::Bound::new(self)
}
}
impl<__DB, __ST> diesel::deserialize::Queryable<__ST, __DB> for
AutoVacuumMode where __DB: diesel::backend::Backend,
__ST: diesel::sql_types::SingleValue,
Self: diesel::deserialize::FromSql<__ST, __DB> {
type Row = Self;
fn build(row: Self) -> diesel::deserialize::Result<Self> {
diesel::deserialize::Result::Ok(row)
}
}
};crate::types::Enum)]
395#[diesel(sql_type = crate::sql_types::Integer)]
396#[non_exhaustive]
397#[repr(i32)]
398pub enum AutoVacuumMode {
399 None = 0,
401 Full = 1,
403 Incremental = 2,
406}
407
408impl SqliteConnection {
409 pub fn immediate_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
431 where
432 F: FnOnce(&mut Self) -> Result<T, E>,
433 E: From<Error>,
434 {
435 self.transaction_sql(f, "BEGIN IMMEDIATE")
436 }
437
438 pub fn exclusive_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
460 where
461 F: FnOnce(&mut Self) -> Result<T, E>,
462 E: From<Error>,
463 {
464 self.transaction_sql(f, "BEGIN EXCLUSIVE")
465 }
466
467 pub fn last_insert_rowid(&self) -> Option<NonZeroI64> {
503 NonZeroI64::new(self.raw_connection.last_insert_rowid())
504 }
505
506 pub fn get_read_only_blob<'conn, 'query, U>(
535 &'conn self,
536 blob_column: U,
537 row_id: i64,
538 ) -> Result<sqlite_blob::SqliteReadOnlyBlob<'conn>, Error>
539 where
540 'query: 'conn,
541 U: ColumnHasTable,
542 U::Table: NamedTable,
543 {
544 let table = blob_column.table();
545
546 let database_name = table.schema().unwrap_or("main");
547 let column_name = blob_column.name();
548 let table_name = table.table();
549
550 self.raw_connection
551 .blob_open(database_name, table_name, column_name, row_id)
552 }
553
554 fn transaction_sql<T, E, F>(&mut self, f: F, sql: &str) -> Result<T, E>
555 where
556 F: FnOnce(&mut Self) -> Result<T, E>,
557 E: From<Error>,
558 {
559 AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?;
560 match f(&mut *self) {
561 Ok(value) => {
562 AnsiTransactionManager::commit_transaction(&mut *self)?;
563 Ok(value)
564 }
565 Err(e) => {
566 AnsiTransactionManager::rollback_transaction(&mut *self)?;
567 Err(e)
568 }
569 }
570 }
571
572 fn prepared_query<'conn, 'query, T>(
573 &'conn mut self,
574 source: T,
575 ) -> QueryResult<StatementUse<'conn, 'query>>
576 where
577 T: QueryFragment<Sqlite> + QueryId + 'query,
578 {
579 self.instrumentation
580 .on_connection_event(InstrumentationEvent::StartQuery {
581 query: &crate::debug_query(&source),
582 });
583 let raw_connection = &self.raw_connection;
584 let cache = &mut self.statement_cache;
585 let statement = match cache.cached_statement(
586 &source,
587 &Sqlite,
588 &[],
589 raw_connection,
590 Statement::prepare,
591 &mut *self.instrumentation,
592 ) {
593 Ok(statement) => statement,
594 Err(e) => {
595 self.instrumentation
596 .on_connection_event(InstrumentationEvent::FinishQuery {
597 query: &crate::debug_query(&source),
598 error: Some(&e),
599 });
600
601 return Err(e);
602 }
603 };
604
605 StatementUse::bind(statement, source, &mut *self.instrumentation)
606 }
607
608 #[doc(hidden)]
609 pub fn register_sql_function<ArgsSqlType, RetSqlType, Args, Ret, F>(
610 &mut self,
611 fn_name: &str,
612 behavior: SqliteFunctionBehavior,
613 mut f: F,
614 ) -> QueryResult<()>
615 where
616 F: FnMut(Args) -> Ret + core::panic::UnwindSafe + Send + 'static,
617 Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
618 Ret: ToSql<RetSqlType, Sqlite>,
619 Sqlite: HasSqlType<RetSqlType>,
620 {
621 functions::register(&self.raw_connection, fn_name, behavior, move |_, args| {
622 f(args)
623 })
624 }
625
626 #[doc(hidden)]
627 pub fn register_noarg_sql_function<RetSqlType, Ret, F>(
628 &mut self,
629 fn_name: &str,
630 behavior: SqliteFunctionBehavior,
631 f: F,
632 ) -> QueryResult<()>
633 where
634 F: FnMut() -> Ret + core::panic::UnwindSafe + Send + 'static,
635 Ret: ToSql<RetSqlType, Sqlite>,
636 Sqlite: HasSqlType<RetSqlType>,
637 {
638 functions::register_noargs(&self.raw_connection, fn_name, behavior, f)
639 }
640
641 #[doc(hidden)]
642 pub fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
643 &mut self,
644 fn_name: &str,
645 behavior: SqliteFunctionBehavior,
646 ) -> QueryResult<()>
647 where
648 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
649 Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
650 Ret: ToSql<RetSqlType, Sqlite>,
651 Sqlite: HasSqlType<RetSqlType>,
652 {
653 functions::register_aggregate::<_, _, _, _, A>(&self.raw_connection, fn_name, behavior)
654 }
655
656 pub fn register_collation<F>(&mut self, collation_name: &str, collation: F) -> QueryResult<()>
692 where
693 F: Fn(&str, &str) -> core::cmp::Ordering + Send + 'static + core::panic::UnwindSafe,
694 {
695 self.raw_connection
696 .register_collation_function(collation_name, collation)
697 }
698
699 pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase {
708 self.raw_connection.serialize()
709 }
710
711 #[allow(unsafe_code)]
751 pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> {
752 self.serialized_data.push(data.to_vec());
755 let last = self
756 .serialized_data
757 .last()
758 .expect("We literally pushed it above, so it's there");
759 unsafe {
760 self.raw_connection.deserialize(last)
763 }
764 }
765
766 #[allow(unsafe_code)]
853 pub unsafe fn with_raw_connection<R, F>(&mut self, f: F) -> R
854 where
855 F: FnOnce(*mut ffi::sqlite3) -> R,
856 {
857 f(self.raw_connection.internal_connection.as_ptr())
858 }
859
860 #[allow(unsafe_code)]
869 pub(crate) unsafe fn with_borrowed_connection<R>(
870 db: core::ptr::NonNull<ffi::sqlite3>,
871 f: impl FnOnce(&mut SqliteConnection) -> R,
872 ) -> R {
873 struct Borrowed(core::mem::ManuallyDrop<SqliteConnection>);
876
877 impl Drop for Borrowed {
878 fn drop(&mut self) {
879 let conn = unsafe { core::mem::ManuallyDrop::take(&mut self.0) };
881 let SqliteConnection {
882 statement_cache,
883 raw_connection,
884 ..
885 } = conn;
886 drop(statement_cache);
889 core::mem::forget(raw_connection);
890 }
891 }
892
893 let mut conn = Borrowed(core::mem::ManuallyDrop::new(SqliteConnection {
894 statement_cache: StatementCache::new(),
895 raw_connection: RawConnection::from_ptr(db),
896 transaction_state: AnsiTransactionManager::default(),
897 metadata_lookup: (),
898 instrumentation: DynInstrumentation::default_instrumentation(),
899 serialized_data: Vec::new(),
900 }));
901
902 let result = f(&mut conn.0);
903
904 if true {
if !#[allow(non_exhaustive_omitted_patterns)] match AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0).transaction_depth()
{
Ok(None) => true,
_ => false,
} {
{
::core::panicking::panic_fmt(format_args!("callback must not leave an open transaction on the borrowed connection"));
}
};
};debug_assert!(
907 matches!(
908 AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0)
909 .transaction_depth(),
910 Ok(None)
911 ),
912 "callback must not leave an open transaction on the borrowed connection"
913 );
914
915 result
916 }
917
918 pub fn set_limit(&mut self, limit: SqliteLimit, value: i32) -> i32 {
941 self.raw_connection.set_limit(limit, value)
942 }
943
944 pub fn get_limit(&self, limit: SqliteLimit) -> i32 {
962 self.raw_connection.get_limit(limit)
963 }
964
965 pub fn set_recommended_security_limits(&mut self) {
1008 self.set_limit(SqliteLimit::Length, SqliteLimit::SAFE_LENGTH_LIMIT);
1009 self.set_limit(SqliteLimit::SqlLength, SqliteLimit::SAFE_SQL_LENGTH_LIMIT);
1010 self.set_limit(
1011 SqliteLimit::ColumnCount,
1012 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT,
1013 );
1014 self.set_limit(SqliteLimit::ExprDepth, SqliteLimit::SAFE_EXPR_DEPTH_LIMIT);
1015 self.set_limit(
1016 SqliteLimit::CompoundSelect,
1017 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT,
1018 );
1019 self.set_limit(SqliteLimit::VdbeOp, SqliteLimit::SAFE_VDBE_OP_LIMIT);
1020 self.set_limit(
1021 SqliteLimit::FunctionArg,
1022 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT,
1023 );
1024 self.set_limit(SqliteLimit::Attached, SqliteLimit::SAFE_ATTACHED_LIMIT);
1025 self.set_limit(
1026 SqliteLimit::LikePatternLength,
1027 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT,
1028 );
1029 self.set_limit(
1030 SqliteLimit::VariableNumber,
1031 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT,
1032 );
1033 self.set_limit(
1034 SqliteLimit::TriggerDepth,
1035 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT,
1036 );
1037 }
1038
1039 pub fn set_defensive(&mut self, enabled: bool) -> QueryResult<()> {
1066 self.raw_connection
1067 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE, enabled)
1068 }
1069
1070 pub fn is_defensive(&self) -> QueryResult<bool> {
1074 self.raw_connection
1075 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE)
1076 }
1077
1078 pub fn set_trusted_schema(&mut self, trusted: bool) -> QueryResult<()> {
1090 self.raw_connection
1091 .set_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA, trusted)
1092 }
1093
1094 pub fn is_trusted_schema(&self) -> QueryResult<bool> {
1098 self.raw_connection
1099 .get_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA)
1100 }
1101
1102 pub fn with_load_extension_enabled<R, E>(
1130 &mut self,
1131 f: impl FnOnce(&mut Self) -> Result<R, E>,
1132 ) -> Result<R, E>
1133 where
1134 E: From<crate::result::Error>,
1135 {
1136 self.set_load_extension_enabled(true)?;
1137
1138 #[cfg(feature = "std")]
1142 {
1143 match std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| f(self))) {
1144 Ok(r) => {
1145 self.set_load_extension_enabled(false)?;
1146 r
1147 }
1148 Err(panic) => {
1149 let _ = self.set_load_extension_enabled(false);
1150 std::panic::resume_unwind(panic);
1151 }
1152 }
1153 }
1154 #[cfg(not(feature = "std"))]
1155 {
1156 let r = f(self);
1157 self.set_load_extension_enabled(false)?;
1158 r
1159 }
1160 }
1161
1162 fn set_load_extension_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1163 self.raw_connection
1164 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, enabled)
1165 }
1166
1167 #[cfg(test)]
1168 fn is_load_extension_enabled(&self) -> QueryResult<bool> {
1169 self.raw_connection
1170 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION)
1171 }
1172
1173 pub fn set_fts3_tokenizer_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1182 self.raw_connection
1183 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, enabled)
1184 }
1185
1186 pub fn is_fts3_tokenizer_enabled(&self) -> QueryResult<bool> {
1190 self.raw_connection
1191 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER)
1192 }
1193
1194 pub fn set_writable_schema(&mut self, enabled: bool) -> QueryResult<()> {
1203 self.raw_connection
1204 .set_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA, enabled)
1205 }
1206
1207 pub fn is_writable_schema(&self) -> QueryResult<bool> {
1211 self.raw_connection
1212 .get_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA)
1213 }
1214
1215 pub fn set_attach_create_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1223 self.raw_connection
1224 .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, enabled)
1225 }
1226
1227 pub fn is_attach_create_enabled(&self) -> QueryResult<bool> {
1231 self.raw_connection
1232 .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE)
1233 }
1234
1235 pub fn set_attach_write_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1242 self.raw_connection
1243 .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, enabled)
1244 }
1245
1246 pub fn is_attach_write_enabled(&self) -> QueryResult<bool> {
1250 self.raw_connection
1251 .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE)
1252 }
1253
1254 pub fn attach_database(&mut self, path: &str, schema_name: &str) -> QueryResult<()> {
1285 use crate::query_dsl::RunQueryDsl;
1286 AttachDatabase { path, schema_name }
1287 .execute(self)
1288 .map(|_| ())
1289 }
1290
1291 pub fn detach_database(&mut self, schema_name: &str) -> QueryResult<()> {
1297 use crate::query_dsl::RunQueryDsl;
1298 DetachDatabase { schema_name }.execute(self).map(|_| ())
1299 }
1300
1301 pub fn set_triggers_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1307 self.raw_connection
1308 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER, enabled)
1309 }
1310
1311 pub fn are_triggers_enabled(&self) -> QueryResult<bool> {
1315 self.raw_connection
1316 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER)
1317 }
1318
1319 pub fn set_views_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1325 self.raw_connection
1326 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW, enabled)
1327 }
1328
1329 pub fn are_views_enabled(&self) -> QueryResult<bool> {
1333 self.raw_connection
1334 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW)
1335 }
1336
1337 pub fn set_foreign_keys_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1343 self.raw_connection
1344 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY, enabled)
1345 }
1346
1347 pub fn are_foreign_keys_enabled(&self) -> QueryResult<bool> {
1351 self.raw_connection
1352 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY)
1353 }
1354
1355 pub fn set_double_quoted_strings_dml(&mut self, enabled: bool) -> QueryResult<()> {
1363 self.raw_connection
1364 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML, enabled)
1365 }
1366
1367 pub fn are_double_quoted_strings_dml_enabled(&self) -> QueryResult<bool> {
1371 self.raw_connection
1372 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML)
1373 }
1374
1375 pub fn set_double_quoted_strings_ddl(&mut self, enabled: bool) -> QueryResult<()> {
1383 self.raw_connection
1384 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL, enabled)
1385 }
1386
1387 pub fn are_double_quoted_strings_ddl_enabled(&self) -> QueryResult<bool> {
1391 self.raw_connection
1392 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL)
1393 }
1394
1395 pub fn auto_vacuum(&mut self, schema: Option<&str>) -> QueryResult<AutoVacuumMode> {
1412 use crate::query_dsl::RunQueryDsl;
1413 let query: Pragma<'_, crate::sql_types::Integer> = Pragma::new("auto_vacuum", schema);
1414 query.get_result(self)
1415 }
1416
1417 pub fn set_auto_vacuum(
1437 &mut self,
1438 schema: Option<&str>,
1439 mode: AutoVacuumMode,
1440 ) -> QueryResult<()> {
1441 use crate::query_dsl::RunQueryDsl;
1442 SetPragmaInt {
1444 schema,
1445 name: "auto_vacuum",
1446 value: mode as i32,
1447 }
1448 .execute(self)
1449 .map(|_| ())
1450 }
1451
1452 pub fn page_count(&mut self, schema: Option<&str>) -> QueryResult<i64> {
1473 self.read_pragma_count("page_count", schema)
1474 }
1475
1476 pub fn freelist_count(&mut self, schema: Option<&str>) -> QueryResult<i64> {
1493 self.read_pragma_count("freelist_count", schema)
1494 }
1495
1496 fn read_pragma_count(
1497 &mut self,
1498 pragma: &'static str,
1499 schema: Option<&str>,
1500 ) -> QueryResult<i64> {
1501 use crate::query_dsl::RunQueryDsl;
1502
1503 let query: Pragma<'_, crate::sql_types::BigInt> = Pragma::new(pragma, schema);
1504 query.get_result(self)
1505 }
1506
1507 pub fn incremental_vacuum(
1532 &mut self,
1533 schema: Option<&str>,
1534 pages: Option<u32>,
1535 ) -> QueryResult<()> {
1536 use crate::connection::SimpleConnection;
1537 use crate::query_builder::QueryBuilder;
1538 use crate::sqlite::SqliteQueryBuilder;
1539
1540 let mut query = SqliteQueryBuilder::new();
1545 query.push_sql("PRAGMA ");
1546 query.push_identifier(schema.unwrap_or("main"))?;
1547 query.push_sql(".incremental_vacuum");
1548 if let Some(pages) = pages {
1549 query.push_sql("(");
1550 query.push_sql(&pages.to_string());
1551 query.push_sql(")");
1552 }
1553 self.batch_execute(&query.finish())
1554 }
1555
1556 pub fn vacuum(&mut self, schema: Option<&str>) -> QueryResult<()> {
1578 use crate::query_dsl::RunQueryDsl;
1579
1580 Vacuum { schema, into: None }.execute(self).map(|_| ())
1581 }
1582
1583 pub fn vacuum_into(&mut self, schema: Option<&str>, path: &str) -> QueryResult<()> {
1609 use crate::query_dsl::RunQueryDsl;
1610
1611 Vacuum {
1612 schema,
1613 into: Some(path),
1614 }
1615 .execute(self)
1616 .map(|_| ())
1617 }
1618
1619 fn register_diesel_sql_functions(&self) -> QueryResult<()> {
1620 use crate::sql_types::{Integer, Text};
1621
1622 functions::register::<Text, Integer, _, _, _>(
1626 &self.raw_connection,
1627 "diesel_manage_updated_at",
1628 SqliteFunctionBehavior::DIRECTONLY,
1629 |conn, table_name: String| {
1630 conn.exec(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("CREATE TRIGGER __diesel_manage_updated_at_{0}\nAFTER UPDATE ON {0}\nFOR EACH ROW WHEN\n old.updated_at IS NULL AND\n new.updated_at IS NULL OR\n old.updated_at == new.updated_at\nBEGIN\n UPDATE {0}\n SET updated_at = CURRENT_TIMESTAMP\n WHERE ROWID = new.ROWID;\nEND\n",
table_name))
})alloc::format!(
1631 include_str!("diesel_manage_updated_at.sql"),
1632 table_name = table_name
1633 ))
1634 .expect("Failed to create trigger");
1635 0 },
1637 )
1638 }
1639
1640 fn establish_inner(database_url: &str) -> Result<SqliteConnection, ConnectionError> {
1641 use crate::result::ConnectionError::CouldntSetupConfiguration;
1642 let raw_connection = RawConnection::establish(database_url)?;
1643 let conn = Self {
1644 statement_cache: StatementCache::new(),
1645 raw_connection,
1646 transaction_state: AnsiTransactionManager::default(),
1647 metadata_lookup: (),
1648 instrumentation: DynInstrumentation::none(),
1649 serialized_data: Vec::new(),
1650 };
1651 conn.register_diesel_sql_functions()
1652 .map_err(CouldntSetupConfiguration)?;
1653 Ok(conn)
1654 }
1655}
1656
1657fn error_message(err_code: libc::c_int) -> &'static str {
1658 ffi::code_to_str(err_code)
1659}
1660
1661#[derive(const _: () =
{
use diesel;
#[allow(non_camel_case_types)]
impl<'a> diesel::query_builder::QueryId for AttachDatabase<'a> {
type QueryId = AttachDatabase<'static>;
const HAS_STATIC_QUERY_ID: bool = true;
const IS_WINDOW_FUNCTION: bool = false;
}
};QueryId)]
1662struct AttachDatabase<'a> {
1663 path: &'a str,
1664 schema_name: &'a str,
1665}
1666
1667impl QueryFragment<Sqlite> for AttachDatabase<'_> {
1668 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1669 out.push_sql("ATTACH DATABASE ");
1670 out.push_bind_param::<crate::sql_types::Text, _>(self.path)?;
1671 out.push_sql(" AS ");
1672 out.push_bind_param::<crate::sql_types::Text, _>(self.schema_name)?;
1673 Ok(())
1674 }
1675}
1676
1677impl RunQueryDslSupport for AttachDatabase<'_> {}
1678
1679#[derive(const _: () =
{
use diesel;
#[allow(non_camel_case_types)]
impl<'a> diesel::query_builder::QueryId for DetachDatabase<'a> {
type QueryId = DetachDatabase<'static>;
const HAS_STATIC_QUERY_ID: bool = true;
const IS_WINDOW_FUNCTION: bool = false;
}
};QueryId)]
1680struct DetachDatabase<'a> {
1681 schema_name: &'a str,
1682}
1683
1684impl QueryFragment<Sqlite> for DetachDatabase<'_> {
1685 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1686 out.push_sql("DETACH DATABASE ");
1687 out.push_bind_param::<crate::sql_types::Text, _>(self.schema_name)?;
1688 Ok(())
1689 }
1690}
1691
1692impl RunQueryDslSupport for DetachDatabase<'_> {}
1693
1694struct Pragma<'a, ST> {
1698 schema: Option<&'a str>,
1699 name: &'static str,
1700 sql_type: PhantomData<ST>,
1701}
1702
1703impl<'a, ST> Pragma<'a, ST> {
1704 fn new(name: &'static str, schema: Option<&'a str>) -> Self {
1705 Pragma {
1706 schema,
1707 name,
1708 sql_type: PhantomData,
1709 }
1710 }
1711}
1712
1713impl<ST> QueryFragment<Sqlite> for Pragma<'_, ST> {
1714 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1715 out.push_sql("PRAGMA ");
1716 out.push_identifier(self.schema.unwrap_or("main"))?;
1717 out.push_sql(".");
1718 out.push_sql(self.name);
1719 Ok(())
1720 }
1721}
1722
1723impl<ST> QueryId for Pragma<'_, ST> {
1725 type QueryId = ();
1726
1727 const HAS_STATIC_QUERY_ID: bool = false;
1728}
1729
1730impl<ST> Query for Pragma<'_, ST> {
1731 type SqlType = ST;
1732}
1733
1734impl<ST> RunQueryDslSupport for Pragma<'_, ST> {}
1735
1736struct SetPragmaInt<'a> {
1739 schema: Option<&'a str>,
1740 name: &'static str,
1741 value: i32,
1742}
1743
1744impl QueryFragment<Sqlite> for SetPragmaInt<'_> {
1745 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1746 out.push_sql("PRAGMA ");
1747 out.push_identifier(self.schema.unwrap_or("main"))?;
1748 out.push_sql(".");
1749 out.push_sql(self.name);
1750 out.push_sql(" = ");
1751 out.push_sql(&self.value.to_string());
1752 Ok(())
1753 }
1754}
1755
1756impl QueryId for SetPragmaInt<'_> {
1757 type QueryId = ();
1758
1759 const HAS_STATIC_QUERY_ID: bool = false;
1760}
1761
1762impl RunQueryDslSupport for SetPragmaInt<'_> {}
1763
1764struct Vacuum<'a> {
1767 schema: Option<&'a str>,
1768 into: Option<&'a str>,
1769}
1770
1771impl QueryFragment<Sqlite> for Vacuum<'_> {
1772 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1773 out.push_sql("VACUUM ");
1774 out.push_identifier(self.schema.unwrap_or("main"))?;
1775 if let Some(into) = self.into {
1776 out.push_sql(" INTO ");
1777 out.push_bind_param::<crate::sql_types::Text, _>(into)?;
1778 }
1779 Ok(())
1780 }
1781}
1782
1783impl QueryId for Vacuum<'_> {
1785 type QueryId = ();
1786
1787 const HAS_STATIC_QUERY_ID: bool = false;
1788}
1789
1790impl RunQueryDslSupport for Vacuum<'_> {}
1791
1792#[cfg(test)]
1793mod tests {
1794 use super::*;
1795 use crate::dsl::sql;
1796 use crate::prelude::*;
1797 use crate::sql_types::{Integer, Text};
1798 use crate::sqlite::SqliteFunctionBehavior;
1799
1800 fn connection() -> SqliteConnection {
1801 SqliteConnection::establish(":memory:").unwrap()
1802 }
1803
1804 #[diesel_test_helper::test]
1805 #[allow(unsafe_code)]
1806 fn with_raw_connection_can_return_values() {
1807 let connection = &mut connection();
1808
1809 let autocommit_status = unsafe {
1811 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1812 };
1813
1814 assert_ne!(autocommit_status, 0, "Expected autocommit to be enabled");
1816 }
1817
1818 #[diesel_test_helper::test]
1819 #[allow(unsafe_code)]
1820 fn with_raw_connection_works_after_diesel_operations() {
1821 let connection = &mut connection();
1822
1823 crate::sql_query("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")
1825 .execute(connection)
1826 .unwrap();
1827 crate::sql_query("INSERT INTO test_table (value) VALUES ('hello')")
1828 .execute(connection)
1829 .unwrap();
1830
1831 let last_rowid = unsafe {
1833 connection.with_raw_connection(|raw_conn| ffi::sqlite3_last_insert_rowid(raw_conn))
1834 };
1835
1836 assert_eq!(last_rowid, 1, "Last insert rowid should be 1");
1837
1838 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM test_table")
1840 .get_result(connection)
1841 .unwrap();
1842 assert_eq!(count, 1);
1843 }
1844
1845 #[diesel_test_helper::test]
1846 #[allow(unsafe_code)]
1847 fn with_raw_connection_can_execute_raw_sql() {
1848 let connection = &mut connection();
1849
1850 crate::sql_query("CREATE TABLE raw_test (id INTEGER PRIMARY KEY, name TEXT)")
1852 .execute(connection)
1853 .unwrap();
1854
1855 let result = unsafe {
1858 connection.with_raw_connection(|raw_conn| {
1859 let sql = c"INSERT INTO raw_test (name) VALUES ('from_raw')";
1860 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1861 let rc = ffi::sqlite3_exec(
1862 raw_conn,
1863 sql.as_ptr(),
1864 None,
1865 core::ptr::null_mut(),
1866 &mut err_msg,
1867 );
1868 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1869 ffi::sqlite3_free(err_msg as *mut libc::c_void);
1870 }
1871 rc
1872 })
1873 };
1874
1875 assert_eq!(result, ffi::SQLITE_OK, "Raw SQL execution should succeed");
1876
1877 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM raw_test")
1879 .get_result(connection)
1880 .unwrap();
1881 assert_eq!(count, 1);
1882
1883 let name: String = sql::<Text>("SELECT name FROM raw_test WHERE id = 1")
1884 .get_result(connection)
1885 .unwrap();
1886 assert_eq!(name, "from_raw");
1887 }
1888
1889 #[diesel_test_helper::test]
1890 #[allow(unsafe_code)]
1891 fn with_raw_connection_works_within_transaction() {
1892 let connection = &mut connection();
1893
1894 crate::sql_query("CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value INTEGER)")
1895 .execute(connection)
1896 .unwrap();
1897
1898 connection
1899 .transaction::<_, crate::result::Error, _>(|conn| {
1900 crate::sql_query("INSERT INTO txn_test (value) VALUES (42)")
1901 .execute(conn)
1902 .unwrap();
1903
1904 let autocommit = unsafe {
1906 conn.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1907 };
1908
1909 assert_eq!(
1911 autocommit, 0,
1912 "Autocommit should be disabled inside transaction"
1913 );
1914
1915 Ok(())
1916 })
1917 .unwrap();
1918
1919 let autocommit = unsafe {
1921 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1922 };
1923 assert_ne!(
1924 autocommit, 0,
1925 "Autocommit should be enabled after transaction"
1926 );
1927 }
1928
1929 #[diesel_test_helper::test]
1930 #[allow(unsafe_code)]
1931 fn with_raw_connection_can_read_database_filename() {
1932 let connection = &mut connection();
1933
1934 let filename = unsafe {
1936 connection.with_raw_connection(|raw_conn| {
1937 let db_name = c"main";
1938 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1939 if filename_ptr.is_null() {
1940 None
1941 } else {
1942 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1944 Some(cstr.to_string_lossy().into_owned())
1945 }
1946 })
1947 };
1948
1949 assert_eq!(
1952 filename,
1953 Some(String::new()),
1954 "In-memory database filename should be an empty string"
1955 );
1956 }
1957
1958 #[diesel_test_helper::test]
1959 #[allow(unsafe_code)]
1960 fn with_raw_connection_changes_count() {
1961 let connection = &mut connection();
1962
1963 crate::sql_query("CREATE TABLE changes_test (id INTEGER PRIMARY KEY, value INTEGER)")
1964 .execute(connection)
1965 .unwrap();
1966
1967 crate::sql_query("INSERT INTO changes_test (value) VALUES (1), (2), (3)")
1968 .execute(connection)
1969 .unwrap();
1970
1971 let changes = unsafe {
1973 connection.with_raw_connection(|raw_conn| {
1974 let sql = c"UPDATE changes_test SET value = value + 10";
1975 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1976 let rc = ffi::sqlite3_exec(
1977 raw_conn,
1978 sql.as_ptr(),
1979 None,
1980 core::ptr::null_mut(),
1981 &mut err_msg,
1982 );
1983 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1984 ffi::sqlite3_free(err_msg as *mut libc::c_void);
1985 return -1;
1986 }
1987 ffi::sqlite3_changes(raw_conn)
1988 })
1989 };
1990
1991 assert_eq!(changes, 3, "Should have updated 3 rows");
1992
1993 let values: Vec<i32> = sql::<Integer>("SELECT value FROM changes_test ORDER BY id")
1995 .load(connection)
1996 .unwrap();
1997 assert_eq!(values, vec![11, 12, 13]);
1998 }
1999
2000 #[diesel_test_helper::test]
2002 #[allow(unsafe_code)]
2003 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2004 fn with_raw_connection_recovers_after_panic() {
2005 let connection = &mut connection();
2006
2007 crate::sql_query("CREATE TABLE panic_test (id INTEGER PRIMARY KEY, value TEXT)")
2008 .execute(connection)
2009 .unwrap();
2010
2011 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
2013 connection.with_raw_connection(|_raw_conn| {
2014 panic!("intentional panic inside with_raw_connection");
2015 })
2016 }));
2017 assert!(result.is_err(), "Should have caught the panic");
2018
2019 crate::sql_query("INSERT INTO panic_test (value) VALUES ('after_panic')")
2021 .execute(connection)
2022 .unwrap();
2023
2024 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM panic_test")
2025 .get_result(connection)
2026 .unwrap();
2027 assert_eq!(count, 1, "Connection should work after panic in callback");
2028 }
2029
2030 #[diesel_test_helper::test]
2032 #[allow(unsafe_code)]
2033 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2034 fn with_raw_connection_can_read_file_database_filename() {
2035 let dir = std::env::temp_dir().join("diesel_test_filename.db");
2036 let db_path = dir.to_str().unwrap();
2037
2038 let _ = std::fs::remove_file(db_path);
2040
2041 let connection = &mut SqliteConnection::establish(db_path).unwrap();
2042
2043 let filename = unsafe {
2045 connection.with_raw_connection(|raw_conn| {
2046 let db_name = c"main";
2047 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
2048 if filename_ptr.is_null() {
2049 None
2050 } else {
2051 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
2052 Some(cstr.to_string_lossy().into_owned())
2053 }
2054 })
2055 };
2056
2057 let filename = filename.expect("File-based database should have a filename");
2058 assert!(
2059 filename.contains("diesel_test_filename.db"),
2060 "Filename should contain the database name, got: {filename}"
2061 );
2062
2063 let _ = std::fs::remove_file(db_path);
2065 }
2066
2067 #[declare_sql_function]
2068 extern "SQL" {
2069 fn fun_case(x: Text) -> Text;
2070 fn my_add(x: Integer, y: Integer) -> Integer;
2071 fn answer() -> Integer;
2072 fn add_counter(x: Integer) -> Integer;
2073
2074 #[aggregate]
2075 fn my_sum(expr: Integer) -> Integer;
2076 #[aggregate]
2077 fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable<Integer>;
2078 }
2079
2080 #[diesel_test_helper::test]
2081 fn database_serializes_and_deserializes_successfully() {
2082 let expected_users = vec![
2083 (
2084 1,
2085 "John Doe".to_string(),
2086 "john.doe@example.com".to_string(),
2087 ),
2088 (
2089 2,
2090 "Jane Doe".to_string(),
2091 "jane.doe@example.com".to_string(),
2092 ),
2093 ];
2094
2095 let conn1 = &mut connection();
2096 let _ =
2097 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
2098 .execute(conn1);
2099 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
2100 .execute(conn1);
2101
2102 for _i in 0..2 {
2103 let serialized_database = conn1.serialize_database_to_buffer();
2104 let conn2 = &mut connection();
2105 conn2
2106 .deserialize_readonly_database_from_buffer(serialized_database.as_slice())
2107 .unwrap();
2108
2109 let query =
2110 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
2111 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
2112
2113 assert_eq!(expected_users, actual_users);
2114 std::mem::drop(serialized_database);
2118 let query =
2119 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
2120 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
2121
2122 assert_eq!(expected_users, actual_users);
2123 }
2124 }
2125
2126 #[diesel_test_helper::test]
2127 fn database_deserialize_random_bytes() {
2128 let buffer = vec![0, 1, 2, 3, 4];
2129 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
2130
2131 conn.deserialize_readonly_database_from_buffer(&buffer)
2132 .unwrap();
2133
2134 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
2135
2136 assert!(r.is_err());
2137 assert_eq!(r.unwrap_err().to_string(), "file is not a database");
2138
2139 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
2140
2141 let _ =
2142 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
2143 .execute(conn);
2144 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
2145 .execute(conn);
2146
2147 let db = conn.serialize_database_to_buffer();
2148 let mut bad_buffer = db[..100].to_vec();
2150 bad_buffer.extend(b"whatever");
2151 conn.deserialize_readonly_database_from_buffer(&bad_buffer)
2152 .unwrap();
2153
2154 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
2155
2156 assert!(r.is_err());
2157 assert_eq!(
2158 r.unwrap_err().to_string(),
2159 "database disk image is malformed"
2160 );
2161
2162 let mut size_fitting_bad_buffer = db[..100].to_vec();
2164 size_fitting_bad_buffer.extend(
2165 core::iter::repeat(b"abcdefghij")
2166 .flatten()
2167 .take(db.len() - 100),
2168 );
2169 let r = conn.deserialize_readonly_database_from_buffer(&size_fitting_bad_buffer);
2170
2171 assert!(r.is_err());
2172 assert_eq!(
2173 r.unwrap_err().to_string(),
2174 "database disk image is malformed"
2175 );
2176 }
2177
2178 #[diesel_test_helper::test]
2179 fn register_custom_function() {
2180 let connection = &mut connection();
2181 fun_case_utils::register_impl(connection, |x: String| {
2182 x.chars()
2183 .enumerate()
2184 .map(|(i, c)| {
2185 if i % 2 == 0 {
2186 c.to_lowercase().to_string()
2187 } else {
2188 c.to_uppercase().to_string()
2189 }
2190 })
2191 .collect::<String>()
2192 })
2193 .unwrap();
2194
2195 let mapped_string = crate::select(fun_case("foobar"))
2196 .get_result::<String>(connection)
2197 .unwrap();
2198 assert_eq!("fOoBaR", mapped_string);
2199 }
2200
2201 #[diesel_test_helper::test]
2202 fn register_multiarg_function() {
2203 let connection = &mut connection();
2204 my_add_utils::register_impl(connection, |x: i32, y: i32| x + y).unwrap();
2205
2206 let added = crate::select(my_add(1, 2)).get_result::<i32>(connection);
2207 assert_eq!(Ok(3), added);
2208 }
2209
2210 #[diesel_test_helper::test]
2211 fn register_noarg_function() {
2212 let connection = &mut connection();
2213 answer_utils::register_impl(connection, || 42).unwrap();
2214
2215 let answer = crate::select(answer()).get_result::<i32>(connection);
2216 assert_eq!(Ok(42), answer);
2217 }
2218
2219 #[diesel_test_helper::test]
2220 fn register_nondeterministic_noarg_function() {
2221 let connection = &mut connection();
2222 answer_utils::register_nondeterministic_impl(connection, || 42).unwrap();
2223
2224 let answer = crate::select(answer()).get_result::<i32>(connection);
2225 assert_eq!(Ok(42), answer);
2226 }
2227
2228 #[diesel_test_helper::test]
2229 fn register_nondeterministic_function() {
2230 let connection = &mut connection();
2231 let mut y = 0;
2232 add_counter_utils::register_nondeterministic_impl(connection, move |x: i32| {
2233 y += 1;
2234 x + y
2235 })
2236 .unwrap();
2237
2238 let added = crate::select((add_counter(1), add_counter(1), add_counter(1)))
2239 .get_result::<(i32, i32, i32)>(connection);
2240 assert_eq!(Ok((2, 3, 4)), added);
2241 }
2242
2243 #[derive(Default)]
2244 struct MySum {
2245 sum: i32,
2246 }
2247
2248 impl SqliteAggregateFunction<i32> for MySum {
2249 type Output = i32;
2250
2251 fn step(&mut self, expr: i32) {
2252 self.sum += expr;
2253 }
2254
2255 fn finalize(aggregator: Option<Self>) -> Self::Output {
2256 aggregator.map(|a| a.sum).unwrap_or_default()
2257 }
2258 }
2259
2260 table! {
2261 my_sum_example {
2262 id -> Integer,
2263 value -> Integer,
2264 }
2265 }
2266
2267 #[diesel_test_helper::test]
2268 fn register_aggregate_function() {
2269 use self::my_sum_example::dsl::*;
2270
2271 let connection = &mut connection();
2272 crate::sql_query(
2273 "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
2274 )
2275 .execute(connection)
2276 .unwrap();
2277 crate::sql_query("INSERT INTO my_sum_example (value) VALUES (1), (2), (3)")
2278 .execute(connection)
2279 .unwrap();
2280
2281 my_sum_utils::register_impl_with_behavior::<MySum, _>(
2282 connection,
2283 SqliteFunctionBehavior::DETERMINISTIC,
2284 )
2285 .unwrap();
2286
2287 let result = my_sum_example
2288 .select(my_sum(value))
2289 .get_result::<i32>(connection);
2290 assert_eq!(Ok(6), result);
2291 }
2292
2293 #[diesel_test_helper::test]
2294 fn register_aggregate_function_returns_finalize_default_on_empty_set() {
2295 use self::my_sum_example::dsl::*;
2296
2297 let connection = &mut connection();
2298 crate::sql_query(
2299 "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
2300 )
2301 .execute(connection)
2302 .unwrap();
2303
2304 my_sum_utils::register_impl_with_behavior::<MySum, _>(
2305 connection,
2306 SqliteFunctionBehavior::DETERMINISTIC,
2307 )
2308 .unwrap();
2309
2310 let result = my_sum_example
2311 .select(my_sum(value))
2312 .get_result::<i32>(connection);
2313 assert_eq!(Ok(0), result);
2314 }
2315
2316 #[derive(Default)]
2317 struct RangeMax<T> {
2318 max_value: Option<T>,
2319 }
2320
2321 impl<T: Default + Ord + Copy + Clone> SqliteAggregateFunction<(T, T, T)> for RangeMax<T> {
2322 type Output = Option<T>;
2323
2324 fn step(&mut self, (x0, x1, x2): (T, T, T)) {
2325 let max = if x0 >= x1 && x0 >= x2 {
2326 x0
2327 } else if x1 >= x0 && x1 >= x2 {
2328 x1
2329 } else {
2330 x2
2331 };
2332
2333 self.max_value = match self.max_value {
2334 Some(current_max_value) if max > current_max_value => Some(max),
2335 None => Some(max),
2336 _ => self.max_value,
2337 };
2338 }
2339
2340 fn finalize(aggregator: Option<Self>) -> Self::Output {
2341 aggregator?.max_value
2342 }
2343 }
2344
2345 table! {
2346 range_max_example {
2347 id -> Integer,
2348 value1 -> Integer,
2349 value2 -> Integer,
2350 value3 -> Integer,
2351 }
2352 }
2353
2354 #[diesel_test_helper::test]
2355 fn register_aggregate_multiarg_function() {
2356 use self::range_max_example::dsl::*;
2357
2358 let connection = &mut connection();
2359 crate::sql_query(
2360 r#"CREATE TABLE range_max_example (
2361 id integer primary key autoincrement,
2362 value1 integer,
2363 value2 integer,
2364 value3 integer
2365 )"#,
2366 )
2367 .execute(connection)
2368 .unwrap();
2369 crate::sql_query(
2370 "INSERT INTO range_max_example (value1, value2, value3) VALUES (3, 2, 1), (2, 2, 2)",
2371 )
2372 .execute(connection)
2373 .unwrap();
2374
2375 range_max_utils::register_impl_with_behavior::<RangeMax<i32>, _, _, _>(
2376 connection,
2377 SqliteFunctionBehavior::DETERMINISTIC,
2378 )
2379 .unwrap();
2380 let result = range_max_example
2381 .select(range_max(value1, value2, value3))
2382 .get_result::<Option<i32>>(connection)
2383 .unwrap();
2384 assert_eq!(Some(3), result);
2385 }
2386
2387 table! {
2388 my_collation_example {
2389 id -> Integer,
2390 value -> Text,
2391 }
2392 }
2393
2394 #[diesel_test_helper::test]
2395 fn register_collation_function() {
2396 use self::my_collation_example::dsl::*;
2397
2398 let connection = &mut connection();
2399
2400 connection
2401 .register_collation("RUSTNOCASE", |rhs, lhs| {
2402 rhs.to_lowercase().cmp(&lhs.to_lowercase())
2403 })
2404 .unwrap();
2405
2406 crate::sql_query(
2407 "CREATE TABLE my_collation_example (id integer primary key autoincrement, value text collate RUSTNOCASE)",
2408 ).execute(connection)
2409 .unwrap();
2410 crate::sql_query(
2411 "INSERT INTO my_collation_example (value) VALUES ('foo'), ('FOo'), ('f00')",
2412 )
2413 .execute(connection)
2414 .unwrap();
2415
2416 let result = my_collation_example
2417 .filter(value.eq("foo"))
2418 .select(value)
2419 .load::<String>(connection);
2420 assert_eq!(
2421 Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
2422 result.as_ref().map(|vec| vec.as_ref())
2423 );
2424
2425 let result = my_collation_example
2426 .filter(value.eq("FOO"))
2427 .select(value)
2428 .load::<String>(connection);
2429 assert_eq!(
2430 Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
2431 result.as_ref().map(|vec| vec.as_ref())
2432 );
2433
2434 let result = my_collation_example
2435 .filter(value.eq("f00"))
2436 .select(value)
2437 .load::<String>(connection);
2438 assert_eq!(
2439 Ok(&["f00".to_owned()][..]),
2440 result.as_ref().map(|vec| vec.as_ref())
2441 );
2442
2443 let result = my_collation_example
2444 .filter(value.eq("F00"))
2445 .select(value)
2446 .load::<String>(connection);
2447 assert_eq!(
2448 Ok(&["f00".to_owned()][..]),
2449 result.as_ref().map(|vec| vec.as_ref())
2450 );
2451
2452 let result = my_collation_example
2453 .filter(value.eq("oof"))
2454 .select(value)
2455 .load::<String>(connection);
2456 assert_eq!(Ok(&[][..]), result.as_ref().map(|vec| vec.as_ref()));
2457 }
2458
2459 #[diesel_test_helper::test]
2461 fn test_correct_serialization_of_owned_strings() {
2462 use crate::prelude::*;
2463
2464 #[derive(Debug, crate::expression::AsExpression)]
2465 #[diesel(sql_type = diesel::sql_types::Text)]
2466 struct CustomWrapper(String);
2467
2468 impl crate::serialize::ToSql<Text, Sqlite> for CustomWrapper {
2469 fn to_sql<'b>(
2470 &'b self,
2471 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
2472 ) -> crate::serialize::Result {
2473 out.set_value(self.0.to_string());
2474 Ok(crate::serialize::IsNull::No)
2475 }
2476 }
2477
2478 let connection = &mut connection();
2479
2480 let res = crate::select(
2481 CustomWrapper("".into())
2482 .into_sql::<crate::sql_types::Text>()
2483 .nullable(),
2484 )
2485 .get_result::<Option<String>>(connection)
2486 .unwrap();
2487 assert_eq!(res, Some(String::new()));
2488 }
2489
2490 #[diesel_test_helper::test]
2491 fn test_correct_serialization_of_owned_bytes() {
2492 use crate::prelude::*;
2493
2494 #[derive(Debug, crate::expression::AsExpression)]
2495 #[diesel(sql_type = diesel::sql_types::Binary)]
2496 struct CustomWrapper(Vec<u8>);
2497
2498 impl crate::serialize::ToSql<crate::sql_types::Binary, Sqlite> for CustomWrapper {
2499 fn to_sql<'b>(
2500 &'b self,
2501 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
2502 ) -> crate::serialize::Result {
2503 out.set_value(self.0.clone());
2504 Ok(crate::serialize::IsNull::No)
2505 }
2506 }
2507
2508 let connection = &mut connection();
2509
2510 let res = crate::select(
2511 CustomWrapper(Vec::new())
2512 .into_sql::<crate::sql_types::Binary>()
2513 .nullable(),
2514 )
2515 .get_result::<Option<Vec<u8>>>(connection)
2516 .unwrap();
2517 assert_eq!(res, Some(Vec::new()));
2518 }
2519
2520 #[diesel_test_helper::test]
2521 fn correctly_handle_empty_query() {
2522 let check_empty_query_error = |r: crate::QueryResult<usize>| {
2523 assert!(r.is_err());
2524 let err = r.unwrap_err();
2525 assert!(
2526 matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::<crate::result::EmptyQuery>()),
2527 "Expected a query builder error, but got {err}"
2528 );
2529 };
2530 let connection = &mut SqliteConnection::establish(":memory:").unwrap();
2531 check_empty_query_error(crate::sql_query("").execute(connection));
2532 check_empty_query_error(crate::sql_query(" ").execute(connection));
2533 check_empty_query_error(crate::sql_query("\n\t").execute(connection));
2534 check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection));
2535 }
2536
2537 #[diesel_test_helper::test]
2538 fn last_insert_rowid_returns_none_on_fresh_connection() {
2539 let conn = &mut connection();
2540 assert_eq!(conn.last_insert_rowid(), None);
2541 }
2542
2543 #[diesel_test_helper::test]
2544 fn last_insert_rowid_returns_rowid_after_insert() {
2545 let conn = &mut connection();
2546 crate::sql_query("CREATE TABLE li_test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2547 .execute(conn)
2548 .unwrap();
2549
2550 crate::sql_query("INSERT INTO li_test (val) VALUES ('a')")
2551 .execute(conn)
2552 .unwrap();
2553 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2554
2555 crate::sql_query("INSERT INTO li_test (val) VALUES ('b')")
2556 .execute(conn)
2557 .unwrap();
2558 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(2));
2559 }
2560
2561 #[diesel_test_helper::test]
2562 fn last_insert_rowid_unchanged_after_failed_insert() {
2563 let conn = &mut connection();
2564 crate::sql_query(
2565 "CREATE TABLE li_test2 (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)",
2566 )
2567 .execute(conn)
2568 .unwrap();
2569
2570 crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')")
2571 .execute(conn)
2572 .unwrap();
2573 let rowid = conn.last_insert_rowid();
2574 assert_eq!(rowid, NonZeroI64::new(1));
2575
2576 let result = crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')").execute(conn);
2578 assert!(result.is_err());
2579
2580 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2582 }
2583
2584 #[diesel_test_helper::test]
2585 fn last_insert_rowid_with_explicit_rowid() {
2586 let conn = &mut connection();
2587 crate::sql_query("CREATE TABLE li_test3 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2588 .execute(conn)
2589 .unwrap();
2590
2591 crate::sql_query("INSERT INTO li_test3 (id, val) VALUES (42, 'a')")
2592 .execute(conn)
2593 .unwrap();
2594 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(42));
2595 }
2596
2597 #[diesel_test_helper::test]
2598 fn last_insert_rowid_unchanged_after_delete_and_update() {
2599 let conn = &mut connection();
2600 crate::sql_query("CREATE TABLE li_test4 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2601 .execute(conn)
2602 .unwrap();
2603
2604 crate::sql_query("INSERT INTO li_test4 (val) VALUES ('a')")
2605 .execute(conn)
2606 .unwrap();
2607 let rowid = conn.last_insert_rowid();
2608 assert_eq!(rowid, NonZeroI64::new(1));
2609
2610 crate::sql_query("UPDATE li_test4 SET val = 'b' WHERE id = 1")
2611 .execute(conn)
2612 .unwrap();
2613 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2614
2615 crate::sql_query("DELETE FROM li_test4 WHERE id = 1")
2616 .execute(conn)
2617 .unwrap();
2618 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2619 }
2620
2621 #[diesel_test_helper::test]
2622 fn read_bytes_from_blob() {
2623 table! {
2624 blobs {
2625 id -> Integer,
2626 data -> Blob,
2627 data2 -> Blob,
2628 }
2629 }
2630
2631 use std::io::Read;
2632
2633 let conn = &mut connection();
2634
2635 let _ =
2636 crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB, data2 BLOB)")
2637 .execute(conn);
2638
2639 let _ = crate::sql_query(
2640 "INSERT INTO blobs (data, data2) VALUES ('abc', 'def'), ('123', '456')",
2641 )
2642 .execute(conn);
2643
2644 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2645 let mut buf = vec![];
2646 data.read_to_end(&mut buf).unwrap();
2647
2648 assert_eq!(buf, b"abc");
2649
2650 let mut data2 = conn.get_read_only_blob(blobs::data2, 1).unwrap();
2651 let mut buf = vec![];
2652 data2.read_to_end(&mut buf).unwrap();
2653
2654 assert_eq!(buf, b"def");
2655 }
2656
2657 #[diesel_test_helper::test]
2658 fn read_seek_bytes() {
2659 table! {
2660 blobs {
2661 id -> Integer,
2662 data -> Blob,
2663 }
2664 }
2665
2666 use std::io::Read;
2667 use std::io::Seek;
2668 use std::io::SeekFrom;
2669
2670 let conn = &mut connection();
2671
2672 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2673 .execute(conn);
2674
2675 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abcdefghi')").execute(conn);
2676
2677 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2678
2679 let mut buf = [0; 1];
2680 assert_eq!(data.read(&mut buf).unwrap(), 1);
2681 assert_eq!(&buf, b"a");
2682
2683 assert_eq!(data.seek(SeekFrom::Current(1)).unwrap(), 2);
2685
2686 let mut buf = [0; 1];
2687 assert_eq!(data.read(&mut buf).unwrap(), 1);
2688 assert_eq!(&buf, b"c");
2689
2690 assert_eq!(data.seek(SeekFrom::Start(0)).unwrap(), 0);
2692
2693 let mut buf = [0; 1];
2694 assert_eq!(data.read(&mut buf).unwrap(), 1);
2695 assert_eq!(&buf, b"a");
2696
2697 assert_eq!(data.seek(SeekFrom::Current(-10)).unwrap(), 0);
2699
2700 let mut buf = [0; 1];
2701 assert_eq!(data.read(&mut buf).unwrap(), 1);
2702 assert_eq!(&buf, b"a");
2703
2704 data.seek(SeekFrom::Current(100)).unwrap();
2706
2707 let mut buf = [0; 1];
2709 assert_eq!(data.read(&mut buf).unwrap(), 0);
2710 }
2711
2712 #[diesel_test_helper::test]
2713 fn use_conn_after_blob_drop() {
2714 table! {
2715 blobs {
2716 id -> Integer,
2717 data -> Blob,
2718 }
2719 }
2720
2721 let conn = &mut connection();
2722
2723 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2724 .execute(conn);
2725
2726 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abc')").execute(conn);
2727
2728 let data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2729 drop(data);
2730
2731 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('def')").execute(conn);
2732 }
2733
2734 #[diesel_test_helper::test]
2735 fn blob_transaction() {
2736 table! {
2737 blobs {
2738 id -> Integer,
2739 data -> Blob,
2740 }
2741 }
2742
2743 use std::io::Read;
2744
2745 let conn = &mut connection();
2746
2747 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2748 .execute(conn);
2749
2750 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abc')").execute(conn);
2751
2752 {
2753 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2754 let mut buf = vec![];
2755 data.read_to_end(&mut buf).unwrap();
2756 assert_eq!(buf, b"abc");
2757 }
2758
2759 let res = conn.exclusive_transaction(|conn| {
2760 crate::sql_query("UPDATE blobs SET data = 'def' WHERE id = 1").execute(conn)?;
2761
2762 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2763 let mut buf = vec![];
2764 data.read_to_end(&mut buf).unwrap();
2765 assert_eq!(buf, b"def");
2766
2767 Result::<(), _>::Err(Error::RollbackTransaction)
2768 });
2769
2770 assert_eq!(res.unwrap_err(), Error::RollbackTransaction);
2771
2772 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2773 let mut buf = vec![];
2774 data.read_to_end(&mut buf).unwrap();
2775 assert_eq!(buf, b"abc");
2776 }
2777
2778 #[diesel_test_helper::test]
2779 fn aggregate_function_works_with_aligned_data() {
2780 #[derive(Debug, Default)]
2781 #[repr(align(64))]
2782 struct OverAligned;
2783
2784 impl SqliteAggregateFunction<i32> for OverAligned {
2785 type Output = i64;
2786
2787 fn step(&mut self, _value: i32) {
2788 let need = core::mem::align_of::<Self>();
2789 let got = core::mem::align_of_val(self);
2790 assert_eq!(need, got);
2791 }
2792
2793 fn finalize(_agg: Option<Self>) -> i64 {
2794 0
2795 }
2796 }
2797 #[declare_sql_function]
2798 extern "SQL" {
2799 #[aggregate]
2800 fn over_aligned_sum(x: Integer) -> diesel::sql_types::BigInt;
2801 }
2802
2803 let mut conn = SqliteConnection::establish(":memory:").unwrap();
2804 over_aligned_sum_utils::register_impl::<OverAligned, _>(&mut conn).unwrap();
2805
2806 diesel::select(over_aligned_sum(1))
2807 .execute(&mut conn)
2808 .unwrap();
2809 }
2810
2811 #[diesel_test_helper::test]
2812 fn sum_twice() {
2813 #[derive(Default)]
2814 struct Sum(i32);
2815
2816 impl SqliteAggregateFunction<i32> for Sum {
2817 type Output = i32;
2818
2819 fn step(&mut self, value: i32) {
2820 self.0 += value;
2821 }
2822
2823 fn finalize(agg: Option<Self>) -> i32 {
2824 agg.map(|s| s.0).unwrap_or_default()
2825 }
2826 }
2827
2828 #[declare_sql_function]
2829 extern "SQL" {
2830 #[aggregate]
2831 fn my_sum(x: Integer) -> Integer;
2832 }
2833
2834 let mut conn = SqliteConnection::establish(":memory:").unwrap();
2835 my_sum_utils::register_impl::<Sum, _>(&mut conn).unwrap();
2836
2837 conn.batch_execute(
2838 "
2839 CREATE TABLE test(key1 INTEGER, key2 INTEGER);
2840 INSERT INTO test(key1, key2) VALUES (1, 2), (2, 4), (3, 6);
2841",
2842 )
2843 .unwrap();
2844
2845 table! {
2846 test (key1, key2) {
2847 key1 -> Integer,
2848 key2 -> Integer,
2849 }
2850 }
2851
2852 let (first_res, second_res) = test::table
2853 .select((my_sum(test::key1), my_sum(test::key2)))
2854 .get_result::<(i32, i32)>(&mut conn)
2855 .unwrap();
2856
2857 assert_eq!(first_res, 6);
2858 assert_eq!(second_res, 12);
2859
2860 conn.batch_execute("DELETE FROM test").unwrap();
2861 let (first_res, second_res) = test::table
2862 .select((my_sum(test::key1), my_sum(test::key2)))
2863 .get_result::<(i32, i32)>(&mut conn)
2864 .unwrap();
2865
2866 assert_eq!(first_res, 0);
2867 assert_eq!(second_res, 0);
2868 }
2869
2870 #[diesel_test_helper::test]
2871 fn test_injection() {
2872 diesel::table! {
2873 #[sql_name = "quote'table"]
2874 quote_table (id) {
2875 id -> Nullable<Integer>,
2876 name -> Nullable<Text>,
2877 }
2878 }
2879
2880 let mut conn = SqliteConnection::establish(":memory:").unwrap();
2881
2882 conn.batch_execute("CREATE TABLE \"quote'table\" (id INTEGER PRIMARY KEY, name TEXT);")
2883 .unwrap();
2884
2885 diesel::insert_into(quote_table::table)
2886 .values((quote_table::id.eq(1), quote_table::name.eq("Jane")))
2887 .execute(&mut conn)
2888 .unwrap();
2889
2890 let data = quote_table::table
2891 .load::<(Option<i32>, Option<String>)>(&mut conn)
2892 .unwrap();
2893 assert_eq!(data, [(Some(1), Some("Jane".to_owned()))]);
2894 }
2895
2896 #[diesel_test_helper::test]
2897 fn set_limit_returns_previous_value() {
2898 let mut conn = connection();
2899 let original = conn.get_limit(SqliteLimit::SqlLength);
2900
2901 assert_eq!(conn.set_limit(SqliteLimit::SqlLength, 1024), original);
2904 assert_eq!(conn.set_limit(SqliteLimit::SqlLength, 2048), 1024);
2905 assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 2048);
2906 }
2907
2908 #[diesel_test_helper::test]
2909 fn get_limit_does_not_mutate() {
2910 let conn = connection();
2911 let first = conn.get_limit(SqliteLimit::ExprDepth);
2912 assert!(first > 0);
2915 assert_eq!(conn.get_limit(SqliteLimit::ExprDepth), first);
2916 }
2917
2918 #[diesel_test_helper::test]
2919 fn set_limit_enforces_length() {
2920 let mut conn = connection();
2921 conn.set_limit(SqliteLimit::Length, 100);
2922
2923 assert!(
2924 crate::sql_query("SELECT length(randomblob(50))")
2925 .execute(&mut conn)
2926 .is_ok()
2927 );
2928 assert!(
2930 crate::sql_query("SELECT length(randomblob(500))")
2931 .execute(&mut conn)
2932 .is_err()
2933 );
2934 }
2935
2936 #[diesel_test_helper::test]
2937 fn set_limit_enforces_column_count() {
2938 let wide = format!(
2941 "SELECT {}",
2942 (1..=30)
2943 .map(|i| i.to_string())
2944 .collect::<Vec<_>>()
2945 .join(", ")
2946 );
2947
2948 let mut unconstrained = connection();
2949 assert!(crate::sql_query(&wide).execute(&mut unconstrained).is_ok());
2950
2951 let mut conn = connection();
2952 conn.set_limit(SqliteLimit::ColumnCount, 10);
2953 assert!(crate::sql_query(&wide).execute(&mut conn).is_err());
2954 }
2955
2956 #[diesel_test_helper::test]
2957 fn set_limit_enforces_expr_depth() {
2958 let mut conn = connection();
2959 conn.set_limit(SqliteLimit::ExprDepth, 5);
2960
2961 assert!(crate::sql_query("SELECT 1+1").execute(&mut conn).is_ok());
2962 let deep = format!("SELECT {}1", "1+".repeat(40));
2964 assert!(crate::sql_query(&deep).execute(&mut conn).is_err());
2965 }
2966
2967 #[diesel_test_helper::test]
2968 fn set_limit_enforces_compound_select() {
2969 let mut conn = connection();
2970 conn.set_limit(SqliteLimit::CompoundSelect, 2);
2971
2972 assert!(
2973 crate::sql_query("SELECT 1 UNION SELECT 2")
2974 .execute(&mut conn)
2975 .is_ok()
2976 );
2977 assert!(
2979 crate::sql_query(
2980 "SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5"
2981 )
2982 .execute(&mut conn)
2983 .is_err()
2984 );
2985 }
2986
2987 #[diesel_test_helper::test]
2988 fn set_limit_enforces_vdbe_op() {
2989 let heavy = "SELECT count(*) FROM sqlite_master a, sqlite_master b, sqlite_master c";
2992
2993 let mut unconstrained = connection();
2994 assert!(crate::sql_query(heavy).execute(&mut unconstrained).is_ok());
2995
2996 let mut conn = connection();
2997 conn.set_limit(SqliteLimit::VdbeOp, 5);
2998 assert!(crate::sql_query(heavy).execute(&mut conn).is_err());
2999 }
3000
3001 #[diesel_test_helper::test]
3002 fn set_limit_enforces_function_arg() {
3003 let mut conn = connection();
3004 conn.set_limit(SqliteLimit::FunctionArg, 3);
3005
3006 assert!(
3007 crate::sql_query("SELECT max(1, 2, 3)")
3008 .execute(&mut conn)
3009 .is_ok()
3010 );
3011 assert!(
3013 crate::sql_query("SELECT max(1, 2, 3, 4, 5, 6, 7, 8)")
3014 .execute(&mut conn)
3015 .is_err()
3016 );
3017 }
3018
3019 #[diesel_test_helper::test]
3020 fn set_limit_enforces_attached() {
3021 let mut conn = connection();
3022 conn.set_limit(SqliteLimit::Attached, 0);
3023
3024 assert!(
3026 crate::sql_query("ATTACH DATABASE ':memory:' AS aux_db")
3027 .execute(&mut conn)
3028 .is_err()
3029 );
3030 }
3031
3032 #[diesel_test_helper::test]
3033 fn set_limit_enforces_variable_number() {
3034 let mut conn = connection();
3035 conn.set_limit(
3039 SqliteLimit::VariableNumber,
3040 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT,
3041 );
3042 let at_limit = format!("SELECT ?{}", SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT);
3043 let past_limit = format!(
3044 "SELECT ?{}",
3045 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT as i64 + 1
3046 );
3047 assert!(crate::sql_query(&at_limit).execute(&mut conn).is_ok());
3048 assert!(crate::sql_query(&past_limit).execute(&mut conn).is_err());
3049 }
3050
3051 #[diesel_test_helper::test]
3052 fn set_limit_enforces_trigger_depth() {
3053 use crate::connection::SimpleConnection;
3054
3055 let setup = "PRAGMA recursive_triggers = ON;\
3057 CREATE TABLE recur (x INTEGER);\
3058 CREATE TRIGGER recur_tr AFTER INSERT ON recur WHEN NEW.x < 100 \
3059 BEGIN INSERT INTO recur VALUES (NEW.x + 1); END;";
3060
3061 let mut unconstrained = connection();
3063 unconstrained.batch_execute(setup).unwrap();
3064 assert!(
3065 crate::sql_query("INSERT INTO recur VALUES (1)")
3066 .execute(&mut unconstrained)
3067 .is_ok()
3068 );
3069
3070 let mut conn = connection();
3073 conn.set_limit(SqliteLimit::TriggerDepth, 3);
3074 conn.batch_execute(setup).unwrap();
3075 assert!(
3076 crate::sql_query("INSERT INTO recur VALUES (1)")
3077 .execute(&mut conn)
3078 .is_err()
3079 );
3080 }
3081
3082 #[diesel_test_helper::test]
3083 fn worker_threads_limit_has_no_runtime_error_path() {
3084 let mut conn = connection();
3089 conn.set_limit(SqliteLimit::WorkerThreads, 0);
3090 assert_eq!(conn.get_limit(SqliteLimit::WorkerThreads), 0);
3091 assert!(crate::sql_query("SELECT 1").execute(&mut conn).is_ok());
3092 }
3093
3094 #[diesel_test_helper::test]
3095 fn set_limit_enforces_sql_length() {
3096 let mut conn = connection();
3097 conn.set_limit(SqliteLimit::SqlLength, 20);
3098
3099 let result =
3101 crate::sql_query("SELECT * FROM sqlite_master WHERE type = 'table'").execute(&mut conn);
3102 assert!(result.is_err());
3103 }
3104
3105 #[diesel_test_helper::test]
3106 fn set_limit_enforces_like_pattern_length() {
3107 let mut conn = connection();
3108 conn.set_limit(SqliteLimit::LikePatternLength, 100);
3109
3110 assert!(
3111 crate::sql_query("SELECT 'test' LIKE 'te%'")
3112 .execute(&mut conn)
3113 .is_ok()
3114 );
3115
3116 let long_pattern = "%".repeat(200);
3117 let query = format!("SELECT 'test' LIKE '{long_pattern}'");
3118 assert!(crate::sql_query(&query).execute(&mut conn).is_err());
3119 }
3120
3121 #[diesel_test_helper::test]
3122 fn set_limit_clamps_above_compile_time_maximum() {
3123 let mut conn = connection();
3124 conn.set_limit(SqliteLimit::Length, i32::MAX);
3127 let clamped = conn.get_limit(SqliteLimit::Length);
3128 assert!(clamped > 0 && clamped < i32::MAX);
3129 }
3130
3131 #[diesel_test_helper::test]
3132 fn set_recommended_security_limits_applies_documented_table() {
3133 let mut conn = connection();
3134 conn.set_recommended_security_limits();
3135
3136 assert_eq!(conn.get_limit(SqliteLimit::Length), 1_000_000);
3137 assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 100_000);
3138 assert_eq!(conn.get_limit(SqliteLimit::ColumnCount), 100);
3139 assert_eq!(conn.get_limit(SqliteLimit::ExprDepth), 10);
3140 assert_eq!(conn.get_limit(SqliteLimit::CompoundSelect), 3);
3141 assert_eq!(conn.get_limit(SqliteLimit::VdbeOp), 25_000);
3142 assert_eq!(conn.get_limit(SqliteLimit::FunctionArg), 8);
3143 assert_eq!(conn.get_limit(SqliteLimit::Attached), 0);
3144 assert_eq!(conn.get_limit(SqliteLimit::LikePatternLength), 50);
3145 assert_eq!(conn.get_limit(SqliteLimit::VariableNumber), 10);
3146 assert_eq!(conn.get_limit(SqliteLimit::TriggerDepth), 10);
3147 }
3148
3149 #[diesel_test_helper::test]
3150 fn safe_limit_constants_do_not_exceed_defaults() {
3151 let pairs = [
3157 (
3158 SqliteLimit::SAFE_LENGTH_LIMIT,
3159 SqliteLimit::DEFAULT_LENGTH_LIMIT,
3160 ),
3161 (
3162 SqliteLimit::SAFE_SQL_LENGTH_LIMIT,
3163 SqliteLimit::DEFAULT_SQL_LENGTH_LIMIT,
3164 ),
3165 (
3166 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT,
3167 SqliteLimit::DEFAULT_COLUMN_COUNT_LIMIT,
3168 ),
3169 (
3170 SqliteLimit::SAFE_EXPR_DEPTH_LIMIT,
3171 SqliteLimit::DEFAULT_EXPR_DEPTH_LIMIT,
3172 ),
3173 (
3174 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT,
3175 SqliteLimit::DEFAULT_COMPOUND_SELECT_LIMIT,
3176 ),
3177 (
3178 SqliteLimit::SAFE_VDBE_OP_LIMIT,
3179 SqliteLimit::DEFAULT_VDBE_OP_LIMIT,
3180 ),
3181 (
3182 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT,
3183 SqliteLimit::DEFAULT_FUNCTION_ARG_LIMIT,
3184 ),
3185 (
3186 SqliteLimit::SAFE_ATTACHED_LIMIT,
3187 SqliteLimit::DEFAULT_ATTACHED_LIMIT,
3188 ),
3189 (
3190 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT,
3191 SqliteLimit::DEFAULT_LIKE_PATTERN_LENGTH_LIMIT,
3192 ),
3193 (
3194 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT,
3195 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT,
3196 ),
3197 (
3198 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT,
3199 SqliteLimit::DEFAULT_TRIGGER_DEPTH_LIMIT,
3200 ),
3201 (
3202 SqliteLimit::SAFE_WORKER_THREADS_LIMIT,
3203 SqliteLimit::DEFAULT_WORKER_THREADS_LIMIT,
3204 ),
3205 ];
3206 for (safe, default) in pairs {
3207 assert!(
3208 safe <= default,
3209 "safe value {safe} exceeds default {default}"
3210 );
3211 }
3212 }
3213
3214 #[diesel_test_helper::test]
3215 fn safe_limit_constants_match_recommended_setter() {
3216 let mut conn = connection();
3217 conn.set_recommended_security_limits();
3218
3219 assert_eq!(
3220 conn.get_limit(SqliteLimit::Length),
3221 SqliteLimit::SAFE_LENGTH_LIMIT
3222 );
3223 assert_eq!(
3224 conn.get_limit(SqliteLimit::SqlLength),
3225 SqliteLimit::SAFE_SQL_LENGTH_LIMIT
3226 );
3227 assert_eq!(
3228 conn.get_limit(SqliteLimit::ColumnCount),
3229 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT
3230 );
3231 assert_eq!(
3232 conn.get_limit(SqliteLimit::ExprDepth),
3233 SqliteLimit::SAFE_EXPR_DEPTH_LIMIT
3234 );
3235 assert_eq!(
3236 conn.get_limit(SqliteLimit::CompoundSelect),
3237 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT
3238 );
3239 assert_eq!(
3240 conn.get_limit(SqliteLimit::VdbeOp),
3241 SqliteLimit::SAFE_VDBE_OP_LIMIT
3242 );
3243 assert_eq!(
3244 conn.get_limit(SqliteLimit::FunctionArg),
3245 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT
3246 );
3247 assert_eq!(
3248 conn.get_limit(SqliteLimit::Attached),
3249 SqliteLimit::SAFE_ATTACHED_LIMIT
3250 );
3251 assert_eq!(
3252 conn.get_limit(SqliteLimit::LikePatternLength),
3253 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT
3254 );
3255 assert_eq!(
3256 conn.get_limit(SqliteLimit::VariableNumber),
3257 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT
3258 );
3259 assert_eq!(
3260 conn.get_limit(SqliteLimit::TriggerDepth),
3261 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT
3262 );
3263 assert_eq!(
3266 conn.get_limit(SqliteLimit::WorkerThreads),
3267 SqliteLimit::SAFE_WORKER_THREADS_LIMIT
3268 );
3269 }
3270
3271 #[diesel_test_helper::test]
3274 fn db_config_defensive_roundtrip() {
3275 let conn = &mut connection();
3276 conn.set_defensive(true).unwrap();
3277 assert!(conn.is_defensive().unwrap());
3278 conn.set_defensive(false).unwrap();
3279 assert!(!conn.is_defensive().unwrap());
3280 }
3281
3282 #[diesel_test_helper::test]
3283 fn db_config_trusted_schema_roundtrip() {
3284 let conn = &mut connection();
3285 conn.set_trusted_schema(false).unwrap();
3286 assert!(!conn.is_trusted_schema().unwrap());
3287 conn.set_trusted_schema(true).unwrap();
3288 assert!(conn.is_trusted_schema().unwrap());
3289 }
3290
3291 #[diesel_test_helper::test]
3292 fn db_config_with_load_extension_enabled_scopes_the_flag() {
3293 let conn = &mut connection();
3294 conn.with_load_extension_enabled(|conn| {
3295 assert!(conn.is_load_extension_enabled().unwrap());
3297 QueryResult::Ok(())
3298 })
3299 .unwrap();
3300 assert!(!conn.is_load_extension_enabled().unwrap());
3302 }
3303
3304 #[cfg(all(
3305 feature = "std",
3306 not(all(target_family = "wasm", target_os = "unknown"))
3307 ))]
3308 #[diesel_test_helper::test]
3309 fn with_load_extension_enabled_disables_after_panic() {
3310 let conn = &mut connection();
3311 let outcome = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
3312 conn.with_load_extension_enabled(|_conn| -> QueryResult<()> {
3313 panic!("boom inside closure");
3314 })
3315 }));
3316 assert!(outcome.is_err(), "panic should propagate");
3317 assert!(
3318 !conn.is_load_extension_enabled().unwrap(),
3319 "extension loading must be disabled again after a panic"
3320 );
3321 }
3322
3323 #[diesel_test_helper::test]
3324 fn db_config_triggers_roundtrip() {
3325 let conn = &mut connection();
3326 conn.set_triggers_enabled(false).unwrap();
3327 assert!(!conn.are_triggers_enabled().unwrap());
3328 conn.set_triggers_enabled(true).unwrap();
3329 assert!(conn.are_triggers_enabled().unwrap());
3330 }
3331
3332 #[diesel_test_helper::test]
3333 fn db_config_views_roundtrip() {
3334 let conn = &mut connection();
3335 conn.set_views_enabled(false).unwrap();
3336 assert!(!conn.are_views_enabled().unwrap());
3337 conn.set_views_enabled(true).unwrap();
3338 assert!(conn.are_views_enabled().unwrap());
3339 }
3340
3341 #[diesel_test_helper::test]
3342 fn db_config_foreign_keys_roundtrip() {
3343 let conn = &mut connection();
3344 conn.set_foreign_keys_enabled(true).unwrap();
3345 assert!(conn.are_foreign_keys_enabled().unwrap());
3346 conn.set_foreign_keys_enabled(false).unwrap();
3347 assert!(!conn.are_foreign_keys_enabled().unwrap());
3348 }
3349
3350 #[diesel_test_helper::test]
3351 fn db_config_dqs_dml_roundtrip() {
3352 let conn = &mut connection();
3353 conn.set_double_quoted_strings_dml(false).unwrap();
3354 assert!(!conn.are_double_quoted_strings_dml_enabled().unwrap());
3355 conn.set_double_quoted_strings_dml(true).unwrap();
3356 assert!(conn.are_double_quoted_strings_dml_enabled().unwrap());
3357 }
3358
3359 #[diesel_test_helper::test]
3360 fn db_config_dqs_ddl_roundtrip() {
3361 let conn = &mut connection();
3362 conn.set_double_quoted_strings_ddl(false).unwrap();
3363 assert!(!conn.are_double_quoted_strings_ddl_enabled().unwrap());
3364 conn.set_double_quoted_strings_ddl(true).unwrap();
3365 assert!(conn.are_double_quoted_strings_ddl_enabled().unwrap());
3366 }
3367
3368 #[diesel_test_helper::test]
3369 fn db_config_fts3_tokenizer_roundtrip() {
3370 let conn = &mut connection();
3371 conn.set_fts3_tokenizer_enabled(false).unwrap();
3372 assert!(!conn.is_fts3_tokenizer_enabled().unwrap());
3373 conn.set_fts3_tokenizer_enabled(true).unwrap();
3374 assert!(conn.is_fts3_tokenizer_enabled().unwrap());
3375 }
3376
3377 #[diesel_test_helper::test]
3378 fn db_config_writable_schema_roundtrip() {
3379 let conn = &mut connection();
3380 conn.set_writable_schema(false).unwrap();
3381 assert!(!conn.is_writable_schema().unwrap());
3382 conn.set_writable_schema(true).unwrap();
3383 assert!(conn.is_writable_schema().unwrap());
3384 }
3385
3386 #[diesel_test_helper::test]
3387 fn db_config_attach_create_roundtrip() {
3388 let conn = &mut connection();
3389 if conn.set_attach_create_enabled(false).is_err() {
3391 return;
3392 }
3393 assert!(!conn.is_attach_create_enabled().unwrap());
3394 conn.set_attach_create_enabled(true).unwrap();
3395 assert!(conn.is_attach_create_enabled().unwrap());
3396 }
3397
3398 #[diesel_test_helper::test]
3399 fn db_config_attach_write_roundtrip() {
3400 let conn = &mut connection();
3401 if conn.set_attach_write_enabled(false).is_err() {
3403 return;
3404 }
3405 assert!(!conn.is_attach_write_enabled().unwrap());
3406 conn.set_attach_write_enabled(true).unwrap();
3407 assert!(conn.is_attach_write_enabled().unwrap());
3408 }
3409
3410 #[diesel_test_helper::test]
3413 fn defensive_mode_blocks_writable_schema() {
3414 let conn = &mut connection();
3415 conn.set_defensive(true).unwrap();
3416 let _ = crate::sql_query("PRAGMA writable_schema = ON").execute(conn);
3418 assert!(!conn.is_writable_schema().unwrap());
3419 }
3420
3421 #[diesel_test_helper::test]
3422 fn foreign_keys_enabled_enforces_constraints() {
3423 let conn = &mut connection();
3424 conn.set_foreign_keys_enabled(true).unwrap();
3425
3426 crate::sql_query("CREATE TABLE parent (id INTEGER PRIMARY KEY)")
3427 .execute(conn)
3428 .unwrap();
3429 crate::sql_query(
3430 "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
3431 )
3432 .execute(conn)
3433 .unwrap();
3434
3435 let result =
3437 crate::sql_query("INSERT INTO child (id, parent_id) VALUES (1, 999)").execute(conn);
3438 assert!(result.is_err());
3439 }
3440
3441 #[diesel_test_helper::test]
3442 fn views_disabled_blocks_view_queries() {
3443 let conn = &mut connection();
3444 crate::sql_query("CREATE TABLE base (id INTEGER PRIMARY KEY)")
3445 .execute(conn)
3446 .unwrap();
3447 crate::sql_query("INSERT INTO base (id) VALUES (1)")
3448 .execute(conn)
3449 .unwrap();
3450 crate::sql_query("CREATE VIEW base_view AS SELECT id FROM base")
3451 .execute(conn)
3452 .unwrap();
3453
3454 conn.set_views_enabled(true).unwrap();
3456 assert!(
3457 crate::sql_query("SELECT id FROM base_view")
3458 .execute(conn)
3459 .is_ok()
3460 );
3461
3462 conn.set_views_enabled(false).unwrap();
3464 assert!(
3465 crate::sql_query("SELECT id FROM base_view")
3466 .execute(conn)
3467 .is_err()
3468 );
3469 }
3470
3471 #[diesel_test_helper::test]
3472 fn triggers_disabled_prevents_firing() {
3473 let conn = &mut connection();
3474 crate::sql_query("CREATE TABLE source (id INTEGER PRIMARY KEY)")
3475 .execute(conn)
3476 .unwrap();
3477 crate::sql_query("CREATE TABLE trigger_log (n INTEGER)")
3478 .execute(conn)
3479 .unwrap();
3480 crate::sql_query("CREATE TRIGGER log_insert AFTER INSERT ON source BEGIN INSERT INTO trigger_log (n) VALUES (1); END")
3481 .execute(conn)
3482 .unwrap();
3483
3484 conn.set_triggers_enabled(false).unwrap();
3486 crate::sql_query("INSERT INTO source (id) VALUES (1)")
3487 .execute(conn)
3488 .unwrap();
3489 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
3490 .get_result(conn)
3491 .unwrap();
3492 assert_eq!(0, count, "trigger should not fire while disabled");
3493
3494 conn.set_triggers_enabled(true).unwrap();
3496 crate::sql_query("INSERT INTO source (id) VALUES (2)")
3497 .execute(conn)
3498 .unwrap();
3499 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
3500 .get_result(conn)
3501 .unwrap();
3502 assert_eq!(1, count, "trigger should fire while enabled");
3503 }
3504
3505 #[diesel_test_helper::test]
3506 fn dqs_dml_controls_double_quoted_string_literals() {
3507 let conn = &mut connection();
3508
3509 conn.set_double_quoted_strings_dml(false).unwrap();
3512 let disabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
3513 assert!(disabled.is_err());
3514
3515 conn.set_double_quoted_strings_dml(true).unwrap();
3517 let enabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
3518 assert_eq!(Ok("bare_token".to_owned()), enabled);
3519 }
3520
3521 #[diesel_test_helper::test]
3522 fn dqs_ddl_controls_double_quoted_string_literals() {
3523 let conn = &mut connection();
3524
3525 conn.set_double_quoted_strings_ddl(false).unwrap();
3528 let disabled =
3529 crate::sql_query(r#"CREATE TABLE dqs_off (name TEXT, CHECK (name <> "not_a_column"))"#)
3530 .execute(conn);
3531 assert!(disabled.is_err());
3532
3533 conn.set_double_quoted_strings_ddl(true).unwrap();
3536 let enabled =
3537 crate::sql_query(r#"CREATE TABLE dqs_on (name TEXT, CHECK (name <> "not_a_column"))"#)
3538 .execute(conn);
3539 assert!(enabled.is_ok());
3540 }
3541
3542 #[diesel_test_helper::test]
3543 fn writable_schema_controls_direct_sqlite_master_writes() {
3544 let conn = &mut connection();
3545 crate::sql_query("CREATE TABLE protected (id INTEGER PRIMARY KEY)")
3546 .execute(conn)
3547 .unwrap();
3548
3549 let update =
3550 "UPDATE sqlite_master SET sql = sql WHERE type = 'table' AND name = 'protected'";
3551
3552 conn.set_writable_schema(false).unwrap();
3554 assert!(crate::sql_query(update).execute(conn).is_err());
3555
3556 conn.set_writable_schema(true).unwrap();
3558 assert!(crate::sql_query(update).execute(conn).is_ok());
3559 }
3560
3561 #[diesel_test_helper::test]
3562 fn fts3_tokenizer_disabled_blocks_the_function() {
3563 let conn = &mut connection();
3564
3565 conn.set_fts3_tokenizer_enabled(true).unwrap();
3567 let enabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
3568 .get_result::<Vec<u8>>(conn);
3569 if enabled.is_err() {
3570 return;
3572 }
3573
3574 conn.set_fts3_tokenizer_enabled(false).unwrap();
3576 let disabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
3577 .get_result::<Vec<u8>>(conn);
3578 assert!(disabled.is_err());
3579 }
3580
3581 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3584 fn temp_db_path(name: &str) -> (tempfile::TempDir, std::path::PathBuf) {
3585 let dir = tempfile::tempdir().unwrap();
3586 let path = dir.path().join(name);
3587 (dir, path)
3588 }
3589
3590 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3591 #[diesel_test_helper::test]
3592 fn attach_create_disabled_blocks_new_database_files() {
3593 let conn = &mut connection();
3594
3595 if conn.set_attach_create_enabled(false).is_err() {
3598 return;
3599 }
3600
3601 let (_dir, path) = temp_db_path("create.db");
3602
3603 assert!(
3605 conn.attach_database(path.to_str().unwrap(), "aux_create")
3606 .is_err()
3607 );
3608
3609 conn.set_attach_create_enabled(true).unwrap();
3611 conn.attach_database(path.to_str().unwrap(), "aux_create")
3612 .unwrap();
3613 conn.detach_database("aux_create").unwrap();
3614 }
3615
3616 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3617 #[diesel_test_helper::test]
3618 fn attach_write_disabled_opens_attached_databases_read_only() {
3619 let conn = &mut connection();
3620
3621 if conn.set_attach_write_enabled(false).is_err() {
3625 return;
3626 }
3627
3628 let (_dir, path) = temp_db_path("write.db");
3630 {
3631 let mut seed = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3632 crate::sql_query("CREATE TABLE t (id INTEGER)")
3633 .execute(&mut seed)
3634 .unwrap();
3635 }
3636
3637 conn.attach_database(path.to_str().unwrap(), "aux_write")
3639 .unwrap();
3640 assert!(
3641 crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
3642 .execute(conn)
3643 .is_err()
3644 );
3645 conn.detach_database("aux_write").unwrap();
3646
3647 conn.set_attach_write_enabled(true).unwrap();
3649 conn.attach_database(path.to_str().unwrap(), "aux_write")
3650 .unwrap();
3651 crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
3652 .execute(conn)
3653 .unwrap();
3654 conn.detach_database("aux_write").unwrap();
3655 }
3656
3657 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3660 table! {
3661 attach_owners (id) {
3662 id -> Integer,
3663 name -> Text,
3664 }
3665 }
3666 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3667 table! {
3668 aux.attach_pets (id) {
3669 id -> Integer,
3670 owner_id -> Integer,
3671 name -> Text,
3672 }
3673 }
3674 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3675 allow_tables_to_appear_in_same_query!(attach_owners, attach_pets);
3676 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3677 table! {
3678 attach_marker (id) {
3679 id -> Integer,
3680 }
3681 }
3682 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3683 table! {
3684 ro.readonly_marker (id) {
3685 id -> Integer,
3686 }
3687 }
3688
3689 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3690 #[diesel_test_helper::test]
3691 fn attach_database_supports_cross_schema_join_then_detach() {
3692 use crate::connection::SimpleConnection;
3693
3694 let conn = &mut connection();
3695
3696 conn.attach_database(":memory:", "aux").unwrap();
3697
3698 conn.batch_execute(
3701 "CREATE TABLE attach_owners (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
3702 CREATE TABLE aux.attach_pets (id INTEGER PRIMARY KEY, owner_id INTEGER, name TEXT NOT NULL);",
3703 )
3704 .unwrap();
3705
3706 crate::insert_into(attach_owners::table)
3707 .values(&[
3708 (attach_owners::id.eq(1), attach_owners::name.eq("Sean")),
3709 (attach_owners::id.eq(2), attach_owners::name.eq("Tess")),
3710 ])
3711 .execute(conn)
3712 .unwrap();
3713 crate::insert_into(attach_pets::table)
3714 .values((
3715 attach_pets::id.eq(1),
3716 attach_pets::owner_id.eq(1),
3717 attach_pets::name.eq("Ferris"),
3718 ))
3719 .execute(conn)
3720 .unwrap();
3721
3722 let pet_owner = attach_owners::table
3723 .inner_join(attach_pets::table.on(attach_pets::owner_id.eq(attach_owners::id)))
3724 .filter(attach_pets::name.eq("Ferris"))
3725 .select(attach_owners::name)
3726 .get_result::<String>(conn)
3727 .unwrap();
3728 assert_eq!(pet_owner, "Sean");
3729
3730 conn.detach_database("aux").unwrap();
3731
3732 assert!(
3734 attach_pets::table
3735 .select(attach_pets::name)
3736 .get_result::<String>(conn)
3737 .is_err()
3738 );
3739 }
3740
3741 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3742 #[diesel_test_helper::test]
3743 fn attach_database_binds_path_verbatim_without_quoting() {
3744 let (_dir, path) = temp_db_path("o'brien.db");
3747
3748 conn_attach_roundtrip(&path);
3749
3750 let mut direct = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3753 let count = attach_marker::table
3754 .count()
3755 .get_result::<i64>(&mut direct)
3756 .unwrap();
3757 assert_eq!(count, 0);
3758 }
3759
3760 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3761 fn conn_attach_roundtrip(path: &std::path::Path) {
3762 use crate::connection::SimpleConnection;
3763
3764 let conn = &mut connection();
3765 conn.attach_database(path.to_str().unwrap(), "verbatim")
3766 .unwrap();
3767 conn.batch_execute("CREATE TABLE verbatim.attach_marker (id INTEGER PRIMARY KEY)")
3768 .unwrap();
3769 conn.detach_database("verbatim").unwrap();
3770 }
3771
3772 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3773 #[diesel_test_helper::test]
3774 fn attach_database_interprets_file_uri_query_parameters() {
3775 let (_dir, path) = temp_db_path("uri_seed.db");
3777 {
3778 let mut seed = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3779 crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
3780 .execute(&mut seed)
3781 .unwrap();
3782 crate::sql_query("INSERT INTO t (id) VALUES (1)")
3783 .execute(&mut seed)
3784 .unwrap();
3785 }
3786
3787 let uri = format!("file:{}?mode=ro", path.display());
3791 let conn = &mut connection();
3792 conn.attach_database(&uri, "ro_schema").unwrap();
3793
3794 let id: i64 = sql::<crate::sql_types::BigInt>("SELECT id FROM ro_schema.t")
3796 .get_result(conn)
3797 .unwrap();
3798 assert_eq!(id, 1);
3799
3800 assert!(
3802 crate::sql_query("INSERT INTO ro_schema.t (id) VALUES (2)")
3803 .execute(conn)
3804 .is_err()
3805 );
3806
3807 conn.detach_database("ro_schema").unwrap();
3808 }
3809
3810 #[diesel_test_helper::test]
3811 fn attach_and_detach_surface_errors_without_panicking() {
3812 let conn = &mut connection();
3813
3814 conn.attach_database(":memory:", "dup").unwrap();
3816 assert!(conn.attach_database(":memory:", "dup").is_err());
3817 conn.detach_database("dup").unwrap();
3818
3819 assert!(conn.detach_database("never_attached").is_err());
3823 }
3824
3825 #[diesel_test_helper::test]
3826 fn attach_database_binds_schema_name_verbatim_without_identifier_quoting() {
3827 use crate::connection::SimpleConnection;
3828
3829 let conn = &mut connection();
3830
3831 let schema = "weird 'schema";
3835 conn.attach_database(":memory:", schema).unwrap();
3836
3837 conn.batch_execute(
3838 r#"CREATE TABLE "weird 'schema".t (id INTEGER PRIMARY KEY);
3839 INSERT INTO "weird 'schema".t (id) VALUES (7);"#,
3840 )
3841 .unwrap();
3842 let id = sql::<Integer>(r#"SELECT id FROM "weird 'schema".t"#)
3843 .get_result::<i32>(conn)
3844 .unwrap();
3845 assert_eq!(id, 7);
3846
3847 conn.detach_database(schema).unwrap();
3848 }
3849
3850 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3851 #[diesel_test_helper::test]
3852 fn attach_database_honors_create_and_write_hardening_knobs() {
3853 use crate::connection::SimpleConnection;
3854
3855 let conn = &mut connection();
3856
3857 if conn.set_attach_create_enabled(false).is_err() {
3859 return;
3860 }
3861
3862 let (_dir_missing, missing) = temp_db_path("nocreate.db");
3864 assert!(
3865 conn.attach_database(missing.to_str().unwrap(), "missing")
3866 .is_err()
3867 );
3868 assert!(!missing.exists());
3869
3870 conn.set_attach_write_enabled(false).unwrap();
3872 let (_dir_existing, existing) = temp_db_path("readonly.db");
3873 {
3874 let mut seed = SqliteConnection::establish(existing.to_str().unwrap()).unwrap();
3875 seed.batch_execute("CREATE TABLE readonly_marker (id INTEGER)")
3876 .unwrap();
3877 }
3878 conn.attach_database(existing.to_str().unwrap(), "ro")
3879 .unwrap();
3880 assert!(
3881 crate::insert_into(readonly_marker::table)
3882 .values(readonly_marker::id.eq(1))
3883 .execute(conn)
3884 .is_err()
3885 );
3886 conn.detach_database("ro").unwrap();
3887 }
3888
3889 #[declare_sql_function]
3892 extern "SQL" {
3893 fn directonly_fn() -> Integer;
3894 fn innocuous_fn() -> Integer;
3895 }
3896
3897 #[diesel_test_helper::test]
3898 fn directonly_function_blocked_from_view() {
3899 let conn = &mut connection();
3900
3901 directonly_fn_utils::register_impl_with_behavior(
3903 conn,
3904 SqliteFunctionBehavior::DIRECTONLY,
3905 || 42,
3906 )
3907 .unwrap();
3908
3909 let result = crate::select(directonly_fn()).get_result::<i32>(conn);
3911 assert_eq!(Ok(42), result);
3912
3913 crate::sql_query("CREATE VIEW test_view AS SELECT directonly_fn() AS val")
3915 .execute(conn)
3916 .unwrap();
3917
3918 conn.set_trusted_schema(false).unwrap();
3920
3921 let result = crate::sql_query("SELECT val FROM test_view").execute(conn);
3923 assert!(result.is_err());
3924 }
3925
3926 #[diesel_test_helper::test]
3927 fn innocuous_function_allowed_from_view_with_untrusted_schema() {
3928 let conn = &mut connection();
3929
3930 innocuous_fn_utils::register_impl_with_behavior(
3932 conn,
3933 SqliteFunctionBehavior::DETERMINISTIC | SqliteFunctionBehavior::INNOCUOUS,
3934 || 99,
3935 )
3936 .unwrap();
3937
3938 crate::sql_query("CREATE VIEW innocuous_view AS SELECT innocuous_fn() AS val")
3940 .execute(conn)
3941 .unwrap();
3942
3943 conn.set_trusted_schema(false).unwrap();
3945
3946 let result = crate::sql_query("SELECT val FROM innocuous_view").execute(conn);
3948 assert!(result.is_ok());
3949 }
3950
3951 #[diesel_test_helper::test]
3952 fn auto_vacuum_all_modes_roundtrip_on_fresh_database() {
3953 for mode in [
3954 AutoVacuumMode::None,
3955 AutoVacuumMode::Full,
3956 AutoVacuumMode::Incremental,
3957 ] {
3958 let conn = &mut connection();
3959 conn.set_auto_vacuum(None, mode).unwrap();
3960 assert_eq!(mode, conn.auto_vacuum(None).unwrap());
3961 }
3962 }
3963
3964 #[diesel_test_helper::test]
3965 fn auto_vacuum_incremental_sticks_across_schema_creation() {
3966 let conn = &mut connection();
3967 conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
3968 .unwrap();
3969 assert_eq!(AutoVacuumMode::Incremental, conn.auto_vacuum(None).unwrap());
3970
3971 crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
3972 .execute(conn)
3973 .unwrap();
3974 assert_eq!(
3975 AutoVacuumMode::Incremental,
3976 conn.auto_vacuum(None).unwrap(),
3977 "the mode survives once the schema exists"
3978 );
3979 }
3980
3981 #[diesel_test_helper::test]
3982 fn auto_vacuum_change_from_none_requires_vacuum_on_populated_database() {
3983 let conn = &mut connection();
3984 crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
3985 .execute(conn)
3986 .unwrap();
3987 crate::sql_query("INSERT INTO t (id) VALUES (1)")
3988 .execute(conn)
3989 .unwrap();
3990 assert_eq!(AutoVacuumMode::None, conn.auto_vacuum(None).unwrap());
3991
3992 conn.set_auto_vacuum(None, AutoVacuumMode::Full).unwrap();
3995 assert_eq!(
3996 AutoVacuumMode::None,
3997 conn.auto_vacuum(None).unwrap(),
3998 "the change does not take effect without a VACUUM"
3999 );
4000
4001 crate::sql_query("VACUUM").execute(conn).unwrap();
4002 assert_eq!(
4003 AutoVacuumMode::Full,
4004 conn.auto_vacuum(None).unwrap(),
4005 "VACUUM rewrites the file and applies the mode"
4006 );
4007 }
4008
4009 #[diesel_test_helper::test]
4010 fn auto_vacuum_targets_the_named_attached_database() {
4011 let conn = &mut connection();
4012 crate::sql_query("ATTACH DATABASE ':memory:' AS aux")
4013 .execute(conn)
4014 .unwrap();
4015
4016 conn.set_auto_vacuum(Some("aux"), AutoVacuumMode::Full)
4017 .unwrap();
4018 assert_eq!(AutoVacuumMode::Full, conn.auto_vacuum(Some("aux")).unwrap());
4019 assert_eq!(
4020 AutoVacuumMode::None,
4021 conn.auto_vacuum(None).unwrap(),
4022 "main keeps its own default"
4023 );
4024 }
4025
4026 #[diesel_test_helper::test]
4027 fn auto_vacuum_schema_name_with_double_quote_is_handled() {
4028 let conn = &mut connection();
4029 let schema = r#"we"ird"#;
4030 crate::sql_query(alloc::format!(
4031 r#"ATTACH DATABASE ':memory:' AS "{}""#,
4032 schema.replace('"', "\"\"")
4033 ))
4034 .execute(conn)
4035 .unwrap();
4036
4037 conn.set_auto_vacuum(Some(schema), AutoVacuumMode::Incremental)
4038 .unwrap();
4039 assert_eq!(
4040 AutoVacuumMode::Incremental,
4041 conn.auto_vacuum(Some(schema)).unwrap()
4042 );
4043 }
4044
4045 table! {
4046 pragma_probe (id) {
4047 id -> Integer,
4048 payload -> Text,
4049 }
4050 }
4051
4052 table! {
4053 aux.aux_pragma_probe (id) {
4054 id -> Integer,
4055 payload -> Text,
4056 }
4057 }
4058
4059 const PROBE_TABLE: &str =
4060 "CREATE TABLE pragma_probe (id INTEGER PRIMARY KEY, payload TEXT NOT NULL)";
4061
4062 const AUX_PROBE_TABLE: &str =
4063 "CREATE TABLE aux.aux_pragma_probe (id INTEGER PRIMARY KEY, payload TEXT NOT NULL)";
4064
4065 fn overflowing_payload() -> String {
4068 "x".repeat(64 * 1024)
4069 }
4070
4071 fn insert_overflowing_row(conn: &mut SqliteConnection) {
4072 crate::insert_into(pragma_probe::table)
4073 .values((
4074 pragma_probe::id.eq(1),
4075 pragma_probe::payload.eq(overflowing_payload()),
4076 ))
4077 .execute(conn)
4078 .unwrap();
4079 }
4080
4081 #[diesel_test_helper::test]
4082 fn page_count_is_positive_and_grows() {
4083 let conn = &mut connection();
4084 conn.batch_execute(PROBE_TABLE).unwrap();
4085 let initial = conn.page_count(None).unwrap();
4086 assert!(initial > 0, "an initialized database has at least one page");
4087
4088 insert_overflowing_row(conn);
4089
4090 assert!(
4091 conn.page_count(None).unwrap() > initial,
4092 "a row spanning overflow pages grows the page count"
4093 );
4094 }
4095
4096 #[diesel_test_helper::test]
4097 fn freelist_count_tracks_reclaimable_space() {
4098 let conn = &mut connection();
4099 assert_eq!(
4100 0,
4101 conn.freelist_count(None).unwrap(),
4102 "a fresh database has an empty freelist"
4103 );
4104
4105 conn.batch_execute(PROBE_TABLE).unwrap();
4106 insert_overflowing_row(conn);
4107
4108 crate::delete(pragma_probe::table).execute(conn).unwrap();
4109 assert!(
4110 conn.freelist_count(None).unwrap() > 0,
4111 "deleting the row leaves reclaimable pages on the freelist"
4112 );
4113
4114 crate::sql_query("VACUUM").execute(conn).unwrap();
4116 assert_eq!(
4117 0,
4118 conn.freelist_count(None).unwrap(),
4119 "VACUUM reclaims the freelist"
4120 );
4121 }
4122
4123 #[diesel_test_helper::test]
4124 fn schema_targets_the_named_attached_database() {
4125 let conn = &mut connection();
4126 conn.batch_execute(PROBE_TABLE).unwrap();
4127 conn.attach_database(":memory:", "aux").unwrap();
4128 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4129 crate::insert_into(aux_pragma_probe::table)
4130 .values((
4131 aux_pragma_probe::id.eq(1),
4132 aux_pragma_probe::payload.eq(overflowing_payload()),
4133 ))
4134 .execute(conn)
4135 .unwrap();
4136
4137 let main_pages = conn.page_count(None).unwrap();
4138 let aux_pages = conn.page_count(Some("aux")).unwrap();
4139 assert!(
4140 aux_pages > main_pages,
4141 "the attached database holds the data, main stays small"
4142 );
4143 assert_eq!(
4144 main_pages,
4145 conn.page_count(Some("main")).unwrap(),
4146 "an explicit main matches the default"
4147 );
4148 }
4149
4150 #[diesel_test_helper::test]
4151 fn schema_name_with_backtick_is_escaped() {
4152 let conn = &mut connection();
4155 let schema = "back`tick";
4156 conn.attach_database(":memory:", schema).unwrap();
4157 conn.batch_execute("CREATE TABLE `back``tick`.probe (id INTEGER PRIMARY KEY)")
4158 .unwrap();
4159
4160 assert!(conn.page_count(Some(schema)).unwrap() > 0);
4161 assert_eq!(0, conn.freelist_count(Some(schema)).unwrap());
4162 }
4163
4164 #[diesel_test_helper::test]
4165 fn unknown_schema_is_reported_as_an_error() {
4166 let conn = &mut connection();
4167
4168 assert!(conn.page_count(Some("nope")).is_err());
4169 assert!(conn.freelist_count(Some("nope")).is_err());
4170 }
4171
4172 fn grow_then_empty_freelist(conn: &mut SqliteConnection) {
4175 conn.batch_execute(PROBE_TABLE).unwrap();
4176 let rows = (1..=200)
4177 .map(|id| {
4178 (
4179 pragma_probe::id.eq(id),
4180 pragma_probe::payload.eq("x".repeat(4000)),
4181 )
4182 })
4183 .collect::<Vec<_>>();
4184 crate::insert_into(pragma_probe::table)
4185 .values(rows)
4186 .execute(conn)
4187 .unwrap();
4188 crate::delete(pragma_probe::table).execute(conn).unwrap();
4189 }
4190
4191 fn grow_then_empty_aux_freelist(conn: &mut SqliteConnection) {
4193 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4194 let rows = (1..=200)
4195 .map(|id| {
4196 (
4197 aux_pragma_probe::id.eq(id),
4198 aux_pragma_probe::payload.eq("x".repeat(4000)),
4199 )
4200 })
4201 .collect::<Vec<_>>();
4202 crate::insert_into(aux_pragma_probe::table)
4203 .values(rows)
4204 .execute(conn)
4205 .unwrap();
4206 crate::delete(aux_pragma_probe::table)
4207 .execute(conn)
4208 .unwrap();
4209 }
4210
4211 #[diesel_test_helper::test]
4212 fn incremental_vacuum_clears_the_whole_freelist() {
4213 let conn = &mut connection();
4214 conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4215 .unwrap();
4216 grow_then_empty_freelist(conn);
4217 assert!(
4218 conn.freelist_count(None).unwrap() > 1,
4219 "the deleted rows should leave many pages on the freelist"
4220 );
4221
4222 conn.incremental_vacuum(None, None).unwrap();
4223
4224 assert_eq!(0, conn.freelist_count(None).unwrap());
4227 }
4228
4229 #[diesel_test_helper::test]
4230 fn incremental_vacuum_reclaims_at_most_the_requested_pages() {
4231 let conn = &mut connection();
4232 conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4233 .unwrap();
4234 grow_then_empty_freelist(conn);
4235 let before = conn.freelist_count(None).unwrap();
4236 assert!(before > 10, "the bound has to be smaller than the freelist");
4237
4238 conn.incremental_vacuum(None, Some(10)).unwrap();
4239
4240 let after = conn.freelist_count(None).unwrap();
4241 assert!(after >= before - 10, "at most ten pages may be reclaimed");
4242 assert!(after < before, "some pages should have been reclaimed");
4243 }
4244
4245 #[diesel_test_helper::test]
4246 fn incremental_vacuum_is_a_no_op_outside_incremental_mode() {
4247 let conn = &mut connection();
4248 assert_eq!(AutoVacuumMode::None, conn.auto_vacuum(None).unwrap());
4249 grow_then_empty_freelist(conn);
4250 let before = conn.freelist_count(None).unwrap();
4251 assert!(before > 0);
4252
4253 conn.incremental_vacuum(None, None).unwrap();
4254
4255 assert_eq!(
4256 before,
4257 conn.freelist_count(None).unwrap(),
4258 "a database that is not in incremental mode keeps its freelist"
4259 );
4260 }
4261
4262 #[diesel_test_helper::test]
4263 fn incremental_vacuum_targets_the_named_attached_database() {
4264 let conn = &mut connection();
4265 conn.attach_database(":memory:", "aux").unwrap();
4266 conn.set_auto_vacuum(Some("aux"), AutoVacuumMode::Incremental)
4267 .unwrap();
4268
4269 grow_then_empty_aux_freelist(conn);
4270 assert!(conn.freelist_count(Some("aux")).unwrap() > 0);
4271
4272 conn.incremental_vacuum(Some("aux"), None).unwrap();
4273
4274 assert_eq!(0, conn.freelist_count(Some("aux")).unwrap());
4275 }
4276
4277 #[diesel_test_helper::test]
4278 fn incremental_vacuum_escapes_a_backtick_in_the_schema_name() {
4279 let conn = &mut connection();
4282 let schema = "back`tick";
4283 conn.attach_database(":memory:", schema).unwrap();
4284
4285 conn.incremental_vacuum(Some(schema), None).unwrap();
4286
4287 assert_eq!(0, conn.freelist_count(Some(schema)).unwrap());
4288 }
4289
4290 #[diesel_test_helper::test]
4291 fn incremental_vacuum_of_zero_pages_clears_everything() {
4292 let conn = &mut connection();
4294 conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4295 .unwrap();
4296 grow_then_empty_freelist(conn);
4297 assert!(conn.freelist_count(None).unwrap() > 0);
4298
4299 conn.incremental_vacuum(None, Some(0)).unwrap();
4300
4301 assert_eq!(0, conn.freelist_count(None).unwrap());
4302 }
4303
4304 #[diesel_test_helper::test]
4305 fn incremental_vacuum_of_an_unknown_schema_is_an_error() {
4306 let conn = &mut connection();
4307
4308 assert!(conn.incremental_vacuum(Some("nope"), None).is_err());
4309 }
4310
4311 fn fill_then_delete(conn: &mut SqliteConnection) {
4314 conn.batch_execute(PROBE_TABLE).unwrap();
4315 crate::insert_into(pragma_probe::table)
4316 .values((
4317 pragma_probe::id.eq(1),
4318 pragma_probe::payload.eq("x".repeat(256 * 1024)),
4319 ))
4320 .execute(conn)
4321 .unwrap();
4322 crate::delete(pragma_probe::table).execute(conn).unwrap();
4323 crate::insert_into(pragma_probe::table)
4324 .values((pragma_probe::id.eq(2), pragma_probe::payload.eq("kept")))
4325 .execute(conn)
4326 .unwrap();
4327 }
4328
4329 fn fill_then_delete_aux(conn: &mut SqliteConnection) {
4331 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4332 crate::insert_into(aux_pragma_probe::table)
4333 .values((
4334 aux_pragma_probe::id.eq(1),
4335 aux_pragma_probe::payload.eq("x".repeat(256 * 1024)),
4336 ))
4337 .execute(conn)
4338 .unwrap();
4339 crate::delete(aux_pragma_probe::table)
4340 .execute(conn)
4341 .unwrap();
4342 }
4343
4344 #[diesel_test_helper::test]
4345 fn vacuum_repacks_the_database() {
4346 let conn = &mut connection();
4347 fill_then_delete(conn);
4348 let before = conn.page_count(None).unwrap();
4349 assert!(before > 1);
4350
4351 conn.vacuum(None).unwrap();
4352
4353 assert!(
4354 conn.page_count(None).unwrap() < before,
4355 "rebuilding should release the pages the deleted row occupied"
4356 );
4357 assert_eq!(
4358 1,
4359 pragma_probe::table.count().get_result::<i64>(conn).unwrap(),
4360 "the surviving row is still there"
4361 );
4362 }
4363
4364 #[diesel_test_helper::test]
4365 fn vacuum_targets_the_named_attached_database() {
4366 let conn = &mut connection();
4367 conn.attach_database(":memory:", "aux").unwrap();
4368 fill_then_delete_aux(conn);
4369 let before = conn.page_count(Some("aux")).unwrap();
4370 assert!(before > 1);
4371
4372 conn.vacuum(Some("aux")).unwrap();
4373
4374 assert!(conn.page_count(Some("aux")).unwrap() < before);
4375 }
4376
4377 #[diesel_test_helper::test]
4378 fn vacuum_inside_a_transaction_is_an_error() {
4379 use crate::connection::Connection;
4380
4381 let conn = &mut connection();
4382 let result: QueryResult<()> = conn.transaction(|conn| conn.vacuum(None));
4383
4384 assert!(result.is_err());
4385 }
4386
4387 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4388 #[diesel_test_helper::test]
4389 fn vacuum_into_writes_a_readable_copy_through_a_quoted_path() {
4390 let dir = tempfile::tempdir().unwrap();
4391 let destination = dir.path().join("o'brien backup.db");
4394
4395 let conn = &mut connection();
4396 conn.batch_execute(PROBE_TABLE).unwrap();
4397 crate::insert_into(pragma_probe::table)
4398 .values((pragma_probe::id.eq(1), pragma_probe::payload.eq("copied")))
4399 .execute(conn)
4400 .unwrap();
4401
4402 conn.vacuum_into(None, destination.to_str().unwrap())
4403 .unwrap();
4404
4405 let copy = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4406 assert_eq!(
4407 "copied",
4408 pragma_probe::table
4409 .select(pragma_probe::payload)
4410 .get_result::<String>(copy)
4411 .unwrap()
4412 );
4413 }
4414
4415 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4416 #[diesel_test_helper::test]
4417 fn vacuum_into_refuses_to_overwrite_an_existing_database() {
4418 let dir = tempfile::tempdir().unwrap();
4419 let destination = dir.path().join("occupied.db");
4420 {
4421 let occupied = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4422 occupied.batch_execute(PROBE_TABLE).unwrap();
4423 }
4424
4425 let conn = &mut connection();
4426 conn.batch_execute(PROBE_TABLE).unwrap();
4427
4428 assert!(
4429 conn.vacuum_into(None, destination.to_str().unwrap())
4430 .is_err()
4431 );
4432 }
4433
4434 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4435 #[diesel_test_helper::test]
4436 fn vacuum_into_copies_the_named_attached_database() {
4437 let dir = tempfile::tempdir().unwrap();
4438 let destination = dir.path().join("aux copy.db");
4439
4440 let conn = &mut connection();
4441 conn.attach_database(":memory:", "aux").unwrap();
4442 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4443 crate::insert_into(aux_pragma_probe::table)
4444 .values((
4445 aux_pragma_probe::id.eq(7),
4446 aux_pragma_probe::payload.eq("copied"),
4447 ))
4448 .execute(conn)
4449 .unwrap();
4450
4451 conn.vacuum_into(Some("aux"), destination.to_str().unwrap())
4452 .unwrap();
4453
4454 let copy = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4455 let id = sql::<Integer>("SELECT id FROM aux_pragma_probe")
4458 .get_result::<i32>(copy)
4459 .unwrap();
4460 assert_eq!(7, id);
4461 }
4462
4463 #[diesel_test_helper::test]
4464 fn vacuum_escapes_a_backtick_in_the_schema_name() {
4465 let conn = &mut connection();
4466 let schema = "back`tick";
4467 conn.attach_database(":memory:", schema).unwrap();
4468
4469 conn.vacuum(Some(schema)).unwrap();
4470 }
4471
4472 #[diesel_test_helper::test]
4473 fn vacuuming_two_schemas_rebuilds_each_of_them() {
4474 let conn = &mut connection();
4478 fill_then_delete(conn);
4479 conn.attach_database(":memory:", "aux").unwrap();
4480 fill_then_delete_aux(conn);
4481
4482 let main_before = conn.page_count(None).unwrap();
4483 let aux_before = conn.page_count(Some("aux")).unwrap();
4484
4485 conn.vacuum(None).unwrap();
4486 conn.vacuum(Some("aux")).unwrap();
4487
4488 assert!(
4489 conn.page_count(None).unwrap() < main_before,
4490 "main was rebuilt"
4491 );
4492 assert!(
4493 conn.page_count(Some("aux")).unwrap() < aux_before,
4494 "aux was rebuilt too, not main a second time"
4495 );
4496 }
4497}