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)]
187#[cfg(feature = "__sqlite-shared")]
188pub struct SqliteConnection {
189 statement_cache: StatementCache<Sqlite, Statement>,
193 raw_connection: RawConnection,
194 transaction_state: AnsiTransactionManager,
195 metadata_lookup: (),
198 instrumentation: DynInstrumentation,
199 serialized_data: Vec<Vec<u8>>,
210}
211
212#[allow(unsafe_code)]
216unsafe impl Send for SqliteConnection {}
217
218impl SimpleConnection for SqliteConnection {
219 fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
220 self.instrumentation
221 .on_connection_event(InstrumentationEvent::StartQuery {
222 query: &StrQueryHelper::new(query),
223 });
224 let resp = self.raw_connection.exec(query);
225 self.instrumentation
226 .on_connection_event(InstrumentationEvent::FinishQuery {
227 query: &StrQueryHelper::new(query),
228 error: resp.as_ref().err(),
229 });
230 resp
231 }
232}
233
234impl ConnectionSealed for SqliteConnection {}
235
236impl Connection for SqliteConnection {
237 type Backend = Sqlite;
238 type TransactionManager = AnsiTransactionManager;
239
240 fn establish(database_url: &str) -> ConnectionResult<Self> {
256 let mut instrumentation = DynInstrumentation::default_instrumentation();
257 instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
258 url: database_url,
259 });
260
261 let establish_result = Self::establish_inner(database_url);
262 instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
263 url: database_url,
264 error: establish_result.as_ref().err(),
265 });
266 let mut conn = establish_result?;
267 conn.instrumentation = instrumentation;
268 Ok(conn)
269 }
270
271 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
272 where
273 T: QueryFragment<Self::Backend> + QueryId,
274 {
275 let statement_use = self.prepared_query(source)?;
276 statement_use.run().and_then(|_| {
277 self.raw_connection
278 .rows_affected_by_last_query()
279 .map_err(Error::DeserializationError)
280 })
281 }
282
283 fn transaction_state(&mut self) -> &mut AnsiTransactionManager
284 where
285 Self: Sized,
286 {
287 &mut self.transaction_state
288 }
289
290 fn instrumentation(&mut self) -> &mut dyn Instrumentation {
291 &mut *self.instrumentation
292 }
293
294 fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
295 self.instrumentation = instrumentation.into();
296 }
297
298 fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
299 self.statement_cache.set_cache_size(size);
300 }
301}
302
303impl LoadConnection<DefaultLoadingMode> for SqliteConnection {
304 type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>;
305 type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>;
306
307 fn load<'conn, 'query, T>(
308 &'conn mut self,
309 source: T,
310 ) -> QueryResult<Self::Cursor<'conn, 'query>>
311 where
312 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
313 Self::Backend: QueryMetadata<T::SqlType>,
314 {
315 let statement = self.prepared_query(source)?;
316
317 Ok(StatementIterator::new(statement))
318 }
319}
320
321impl WithMetadataLookup for SqliteConnection {
322 fn metadata_lookup(&mut self) -> &mut <Sqlite as TypeMetadata>::MetadataLookup {
323 &mut self.metadata_lookup
324 }
325}
326
327#[cfg(feature = "r2d2")]
328impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection {
329 fn ping(&mut self) -> QueryResult<()> {
330 use crate::RunQueryDsl;
331
332 crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
333 }
334
335 fn is_broken(&mut self) -> bool {
336 AnsiTransactionManager::is_broken_transaction_manager(self)
337 }
338}
339
340impl MultiConnectionHelper for SqliteConnection {
341 fn to_any<'a>(
342 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
343 ) -> &mut (dyn core::any::Any + 'a) {
344 lookup
345 }
346
347 fn from_any(
348 lookup: &mut dyn core::any::Any,
349 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
350 lookup.downcast_mut()
351 }
352}
353
354#[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)]
357pub enum CommitDecision {
358 Proceed,
360 Rollback,
362}
363
364#[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)]
367pub enum ProgressDecision {
368 Continue,
370 Interrupt,
372}
373
374#[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)]
377pub enum BusyDecision {
378 Retry,
380 GiveUp,
382}
383
384#[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)]
396#[diesel(sql_type = crate::sql_types::Integer)]
397#[non_exhaustive]
398#[repr(i32)]
399pub enum AutoVacuumMode {
400 None = 0,
402 Full = 1,
404 Incremental = 2,
407}
408
409#[derive(#[automatically_derived]
impl ::core::fmt::Debug for WalCheckpointMode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
WalCheckpointMode::Passive => "Passive",
WalCheckpointMode::Full => "Full",
WalCheckpointMode::Restart => "Restart",
WalCheckpointMode::Truncate => "Truncate",
WalCheckpointMode::Noop => "Noop",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for WalCheckpointMode {
#[inline]
fn clone(&self) -> WalCheckpointMode { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for WalCheckpointMode { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for WalCheckpointMode {
#[inline]
fn eq(&self, other: &WalCheckpointMode) -> 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 WalCheckpointMode {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
413#[non_exhaustive]
414pub enum WalCheckpointMode {
415 Passive,
417 Full,
420 Restart,
423 Truncate,
426 Noop,
432}
433
434#[derive(#[automatically_derived]
impl ::core::fmt::Debug for WalCheckpointOutcome {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"WalCheckpointOutcome", "busy", &self.busy, "log_frames",
&self.log_frames, "checkpointed_frames",
&&self.checkpointed_frames)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for WalCheckpointOutcome {
#[inline]
fn clone(&self) -> WalCheckpointOutcome {
let _: ::core::clone::AssertParamIsClone<bool>;
let _: ::core::clone::AssertParamIsClone<Option<i64>>;
let _: ::core::clone::AssertParamIsClone<Option<i64>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for WalCheckpointOutcome { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for WalCheckpointOutcome {
#[inline]
fn eq(&self, other: &WalCheckpointOutcome) -> bool {
self.busy == other.busy && self.log_frames == other.log_frames &&
self.checkpointed_frames == other.checkpointed_frames
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for WalCheckpointOutcome {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<bool>;
let _: ::core::cmp::AssertParamIsEq<Option<i64>>;
let _: ::core::cmp::AssertParamIsEq<Option<i64>>;
}
}Eq)]
436#[non_exhaustive]
437pub struct WalCheckpointOutcome {
438 pub busy: bool,
442 pub log_frames: Option<i64>,
445 pub checkpointed_frames: Option<i64>,
450}
451
452impl SqliteConnection {
453 pub fn immediate_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
475 where
476 F: FnOnce(&mut Self) -> Result<T, E>,
477 E: From<Error>,
478 {
479 self.transaction_sql(f, "BEGIN IMMEDIATE")
480 }
481
482 pub fn exclusive_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
504 where
505 F: FnOnce(&mut Self) -> Result<T, E>,
506 E: From<Error>,
507 {
508 self.transaction_sql(f, "BEGIN EXCLUSIVE")
509 }
510
511 pub fn last_insert_rowid(&self) -> Option<NonZeroI64> {
547 NonZeroI64::new(self.raw_connection.last_insert_rowid())
548 }
549
550 pub fn get_read_only_blob<'conn, 'query, U>(
579 &'conn self,
580 blob_column: U,
581 row_id: i64,
582 ) -> Result<sqlite_blob::SqliteReadOnlyBlob<'conn>, Error>
583 where
584 'query: 'conn,
585 U: ColumnHasTable,
586 U::Table: NamedTable,
587 {
588 let table = blob_column.table();
589
590 let database_name = table.schema().unwrap_or("main");
591 let column_name = blob_column.name();
592 let table_name = table.table();
593
594 self.raw_connection
595 .blob_open(database_name, table_name, column_name, row_id)
596 }
597
598 fn transaction_sql<T, E, F>(&mut self, f: F, sql: &str) -> Result<T, E>
599 where
600 F: FnOnce(&mut Self) -> Result<T, E>,
601 E: From<Error>,
602 {
603 AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?;
604 match f(&mut *self) {
605 Ok(value) => {
606 AnsiTransactionManager::commit_transaction(&mut *self)?;
607 Ok(value)
608 }
609 Err(e) => {
610 AnsiTransactionManager::rollback_transaction(&mut *self)?;
611 Err(e)
612 }
613 }
614 }
615
616 fn prepared_query<'conn, 'query, T>(
617 &'conn mut self,
618 source: T,
619 ) -> QueryResult<StatementUse<'conn, 'query>>
620 where
621 T: QueryFragment<Sqlite> + QueryId + 'query,
622 {
623 self.instrumentation
624 .on_connection_event(InstrumentationEvent::StartQuery {
625 query: &crate::debug_query(&source),
626 });
627 let raw_connection = &self.raw_connection;
628 let cache = &mut self.statement_cache;
629 let statement = match cache.cached_statement(
630 &source,
631 &Sqlite,
632 &[],
633 raw_connection,
634 Statement::prepare,
635 &mut *self.instrumentation,
636 ) {
637 Ok(statement) => statement,
638 Err(e) => {
639 self.instrumentation
640 .on_connection_event(InstrumentationEvent::FinishQuery {
641 query: &crate::debug_query(&source),
642 error: Some(&e),
643 });
644
645 return Err(e);
646 }
647 };
648
649 StatementUse::bind(statement, source, &mut *self.instrumentation)
650 }
651
652 #[doc(hidden)]
653 pub fn register_sql_function<ArgsSqlType, RetSqlType, Args, Ret, F>(
654 &mut self,
655 fn_name: &str,
656 behavior: SqliteFunctionBehavior,
657 mut f: F,
658 ) -> QueryResult<()>
659 where
660 F: FnMut(Args) -> Ret + core::panic::UnwindSafe + Send + 'static,
661 Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
662 Ret: ToSql<RetSqlType, Sqlite>,
663 Sqlite: HasSqlType<RetSqlType>,
664 {
665 functions::register(&self.raw_connection, fn_name, behavior, move |_, args| {
666 f(args)
667 })
668 }
669
670 #[doc(hidden)]
671 pub fn register_noarg_sql_function<RetSqlType, Ret, F>(
672 &mut self,
673 fn_name: &str,
674 behavior: SqliteFunctionBehavior,
675 f: F,
676 ) -> QueryResult<()>
677 where
678 F: FnMut() -> Ret + core::panic::UnwindSafe + Send + 'static,
679 Ret: ToSql<RetSqlType, Sqlite>,
680 Sqlite: HasSqlType<RetSqlType>,
681 {
682 functions::register_noargs(&self.raw_connection, fn_name, behavior, f)
683 }
684
685 #[doc(hidden)]
686 pub fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
687 &mut self,
688 fn_name: &str,
689 behavior: SqliteFunctionBehavior,
690 ) -> QueryResult<()>
691 where
692 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
693 Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
694 Ret: ToSql<RetSqlType, Sqlite>,
695 Sqlite: HasSqlType<RetSqlType>,
696 {
697 functions::register_aggregate::<_, _, _, _, A>(&self.raw_connection, fn_name, behavior)
698 }
699
700 pub fn register_collation<F>(&mut self, collation_name: &str, collation: F) -> QueryResult<()>
736 where
737 F: Fn(&str, &str) -> core::cmp::Ordering + Send + 'static + core::panic::UnwindSafe,
738 {
739 self.raw_connection
740 .register_collation_function(collation_name, collation)
741 }
742
743 pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase {
752 self.raw_connection.serialize()
753 }
754
755 #[allow(unsafe_code)]
795 pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> {
796 self.serialized_data.push(data.to_vec());
799 let last = self
800 .serialized_data
801 .last()
802 .expect("We literally pushed it above, so it's there");
803 unsafe {
804 self.raw_connection.deserialize(last)
807 }
808 }
809
810 #[allow(unsafe_code)]
897 pub unsafe fn with_raw_connection<R, F>(&mut self, f: F) -> R
898 where
899 F: FnOnce(*mut ffi::sqlite3) -> R,
900 {
901 f(self.raw_connection.internal_connection.as_ptr())
902 }
903
904 #[allow(unsafe_code)]
913 pub(crate) unsafe fn with_borrowed_connection<R>(
914 db: core::ptr::NonNull<ffi::sqlite3>,
915 f: impl FnOnce(&mut SqliteConnection) -> R,
916 ) -> R {
917 struct Borrowed(core::mem::ManuallyDrop<SqliteConnection>);
920
921 impl Drop for Borrowed {
922 fn drop(&mut self) {
923 let conn = unsafe { core::mem::ManuallyDrop::take(&mut self.0) };
925 let SqliteConnection {
926 statement_cache,
927 raw_connection,
928 ..
929 } = conn;
930 drop(statement_cache);
933 core::mem::forget(raw_connection);
934 }
935 }
936
937 let mut conn = Borrowed(core::mem::ManuallyDrop::new(SqliteConnection {
938 statement_cache: StatementCache::new(),
939 raw_connection: RawConnection::from_ptr(db),
940 transaction_state: AnsiTransactionManager::default(),
941 metadata_lookup: (),
942 instrumentation: DynInstrumentation::default_instrumentation(),
943 serialized_data: Vec::new(),
944 }));
945
946 let result = f(&mut conn.0);
947
948 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!(
951 matches!(
952 AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0)
953 .transaction_depth(),
954 Ok(None)
955 ),
956 "callback must not leave an open transaction on the borrowed connection"
957 );
958
959 result
960 }
961
962 pub fn set_limit(&mut self, limit: SqliteLimit, value: i32) -> i32 {
985 self.raw_connection.set_limit(limit, value)
986 }
987
988 pub fn get_limit(&self, limit: SqliteLimit) -> i32 {
1006 self.raw_connection.get_limit(limit)
1007 }
1008
1009 pub fn set_recommended_security_limits(&mut self) {
1052 self.set_limit(SqliteLimit::Length, SqliteLimit::SAFE_LENGTH_LIMIT);
1053 self.set_limit(SqliteLimit::SqlLength, SqliteLimit::SAFE_SQL_LENGTH_LIMIT);
1054 self.set_limit(
1055 SqliteLimit::ColumnCount,
1056 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT,
1057 );
1058 self.set_limit(SqliteLimit::ExprDepth, SqliteLimit::SAFE_EXPR_DEPTH_LIMIT);
1059 self.set_limit(
1060 SqliteLimit::CompoundSelect,
1061 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT,
1062 );
1063 self.set_limit(SqliteLimit::VdbeOp, SqliteLimit::SAFE_VDBE_OP_LIMIT);
1064 self.set_limit(
1065 SqliteLimit::FunctionArg,
1066 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT,
1067 );
1068 self.set_limit(SqliteLimit::Attached, SqliteLimit::SAFE_ATTACHED_LIMIT);
1069 self.set_limit(
1070 SqliteLimit::LikePatternLength,
1071 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT,
1072 );
1073 self.set_limit(
1074 SqliteLimit::VariableNumber,
1075 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT,
1076 );
1077 self.set_limit(
1078 SqliteLimit::TriggerDepth,
1079 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT,
1080 );
1081 }
1082
1083 pub fn set_defensive(&mut self, enabled: bool) -> QueryResult<()> {
1110 self.raw_connection
1111 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE, enabled)
1112 }
1113
1114 pub fn is_defensive(&self) -> QueryResult<bool> {
1118 self.raw_connection
1119 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE)
1120 }
1121
1122 pub fn set_trusted_schema(&mut self, trusted: bool) -> QueryResult<()> {
1134 self.raw_connection
1135 .set_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA, trusted)
1136 }
1137
1138 pub fn is_trusted_schema(&self) -> QueryResult<bool> {
1142 self.raw_connection
1143 .get_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA)
1144 }
1145
1146 pub fn with_load_extension_enabled<R, E>(
1174 &mut self,
1175 f: impl FnOnce(&mut Self) -> Result<R, E>,
1176 ) -> Result<R, E>
1177 where
1178 E: From<crate::result::Error>,
1179 {
1180 self.set_load_extension_enabled(true)?;
1181
1182 #[cfg(feature = "std")]
1186 {
1187 match std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| f(self))) {
1188 Ok(r) => {
1189 self.set_load_extension_enabled(false)?;
1190 r
1191 }
1192 Err(panic) => {
1193 let _ = self.set_load_extension_enabled(false);
1194 std::panic::resume_unwind(panic);
1195 }
1196 }
1197 }
1198 #[cfg(not(feature = "std"))]
1199 {
1200 let r = f(self);
1201 self.set_load_extension_enabled(false)?;
1202 r
1203 }
1204 }
1205
1206 fn set_load_extension_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1207 self.raw_connection
1208 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, enabled)
1209 }
1210
1211 #[cfg(test)]
1212 fn is_load_extension_enabled(&self) -> QueryResult<bool> {
1213 self.raw_connection
1214 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION)
1215 }
1216
1217 pub fn set_fts3_tokenizer_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1226 self.raw_connection
1227 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, enabled)
1228 }
1229
1230 pub fn is_fts3_tokenizer_enabled(&self) -> QueryResult<bool> {
1234 self.raw_connection
1235 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER)
1236 }
1237
1238 pub fn set_writable_schema(&mut self, enabled: bool) -> QueryResult<()> {
1247 self.raw_connection
1248 .set_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA, enabled)
1249 }
1250
1251 pub fn is_writable_schema(&self) -> QueryResult<bool> {
1255 self.raw_connection
1256 .get_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA)
1257 }
1258
1259 pub fn set_attach_create_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1267 self.raw_connection
1268 .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, enabled)
1269 }
1270
1271 pub fn is_attach_create_enabled(&self) -> QueryResult<bool> {
1275 self.raw_connection
1276 .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE)
1277 }
1278
1279 pub fn set_attach_write_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1286 self.raw_connection
1287 .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, enabled)
1288 }
1289
1290 pub fn is_attach_write_enabled(&self) -> QueryResult<bool> {
1294 self.raw_connection
1295 .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE)
1296 }
1297
1298 pub fn attach_database(&mut self, path: &str, schema_name: &str) -> QueryResult<()> {
1329 use crate::query_dsl::RunQueryDsl;
1330 AttachDatabase { path, schema_name }
1331 .execute(self)
1332 .map(|_| ())
1333 }
1334
1335 pub fn detach_database(&mut self, schema_name: &str) -> QueryResult<()> {
1341 use crate::query_dsl::RunQueryDsl;
1342 DetachDatabase { schema_name }.execute(self).map(|_| ())
1343 }
1344
1345 pub fn set_triggers_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1351 self.raw_connection
1352 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER, enabled)
1353 }
1354
1355 pub fn are_triggers_enabled(&self) -> QueryResult<bool> {
1359 self.raw_connection
1360 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER)
1361 }
1362
1363 pub fn set_views_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1369 self.raw_connection
1370 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW, enabled)
1371 }
1372
1373 pub fn are_views_enabled(&self) -> QueryResult<bool> {
1377 self.raw_connection
1378 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW)
1379 }
1380
1381 pub fn set_foreign_keys_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1387 self.raw_connection
1388 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY, enabled)
1389 }
1390
1391 pub fn are_foreign_keys_enabled(&self) -> QueryResult<bool> {
1395 self.raw_connection
1396 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY)
1397 }
1398
1399 pub fn set_double_quoted_strings_dml(&mut self, enabled: bool) -> QueryResult<()> {
1407 self.raw_connection
1408 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML, enabled)
1409 }
1410
1411 pub fn are_double_quoted_strings_dml_enabled(&self) -> QueryResult<bool> {
1415 self.raw_connection
1416 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML)
1417 }
1418
1419 pub fn set_double_quoted_strings_ddl(&mut self, enabled: bool) -> QueryResult<()> {
1427 self.raw_connection
1428 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL, enabled)
1429 }
1430
1431 pub fn are_double_quoted_strings_ddl_enabled(&self) -> QueryResult<bool> {
1435 self.raw_connection
1436 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL)
1437 }
1438
1439 pub fn auto_vacuum(&mut self, schema: Option<&str>) -> QueryResult<AutoVacuumMode> {
1456 use crate::query_dsl::RunQueryDsl;
1457 let query: Pragma<'_, crate::sql_types::Integer> = Pragma::new("auto_vacuum", schema);
1458 query.get_result(self)
1459 }
1460
1461 pub fn set_auto_vacuum(
1481 &mut self,
1482 schema: Option<&str>,
1483 mode: AutoVacuumMode,
1484 ) -> QueryResult<()> {
1485 use crate::query_dsl::RunQueryDsl;
1486 SetPragmaInt {
1488 schema,
1489 name: "auto_vacuum",
1490 value: mode as i32,
1491 }
1492 .execute(self)
1493 .map(|_| ())
1494 }
1495
1496 pub fn page_count(&mut self, schema: Option<&str>) -> QueryResult<i64> {
1517 self.read_pragma_count("page_count", schema)
1518 }
1519
1520 pub fn freelist_count(&mut self, schema: Option<&str>) -> QueryResult<i64> {
1537 self.read_pragma_count("freelist_count", schema)
1538 }
1539
1540 fn read_pragma_count(
1541 &mut self,
1542 pragma: &'static str,
1543 schema: Option<&str>,
1544 ) -> QueryResult<i64> {
1545 use crate::query_dsl::RunQueryDsl;
1546
1547 let query: Pragma<'_, crate::sql_types::BigInt> = Pragma::new(pragma, schema);
1548 query.get_result(self)
1549 }
1550
1551 pub fn incremental_vacuum(
1576 &mut self,
1577 schema: Option<&str>,
1578 pages: Option<u32>,
1579 ) -> QueryResult<()> {
1580 use crate::connection::SimpleConnection;
1581 use crate::query_builder::QueryBuilder;
1582 use crate::sqlite::SqliteQueryBuilder;
1583
1584 let mut query = SqliteQueryBuilder::new();
1589 query.push_sql("PRAGMA ");
1590 query.push_identifier(schema.unwrap_or("main"))?;
1591 query.push_sql(".incremental_vacuum");
1592 if let Some(pages) = pages {
1593 query.push_sql("(");
1594 query.push_sql(&pages.to_string());
1595 query.push_sql(")");
1596 }
1597 self.batch_execute(&query.finish())
1598 }
1599
1600 pub fn vacuum(&mut self, schema: Option<&str>) -> QueryResult<()> {
1622 use crate::query_dsl::RunQueryDsl;
1623
1624 Vacuum { schema, into: None }.execute(self).map(|_| ())
1625 }
1626
1627 pub fn vacuum_into(&mut self, schema: Option<&str>, path: &str) -> QueryResult<()> {
1653 use crate::query_dsl::RunQueryDsl;
1654
1655 Vacuum {
1656 schema,
1657 into: Some(path),
1658 }
1659 .execute(self)
1660 .map(|_| ())
1661 }
1662
1663 pub fn wal_checkpoint(
1710 &mut self,
1711 schema: Option<&str>,
1712 mode: WalCheckpointMode,
1713 ) -> QueryResult<WalCheckpointOutcome> {
1714 use crate::query_dsl::RunQueryDsl;
1715
1716 let (busy, log_frames, checkpointed_frames) =
1717 WalCheckpoint { schema, mode }.get_result::<(i32, i64, i64)>(self)?;
1718 Ok(WalCheckpointOutcome {
1719 busy: busy != 0,
1720 log_frames: (log_frames >= 0).then_some(log_frames),
1722 checkpointed_frames: (checkpointed_frames >= 0).then_some(checkpointed_frames),
1723 })
1724 }
1725
1726 fn register_diesel_sql_functions(&self) -> QueryResult<()> {
1727 use crate::sql_types::{Integer, Text};
1728
1729 functions::register::<Text, Integer, _, _, _>(
1733 &self.raw_connection,
1734 "diesel_manage_updated_at",
1735 SqliteFunctionBehavior::DIRECTONLY,
1736 |conn, table_name: String| {
1737 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!(
1738 include_str!("diesel_manage_updated_at.sql"),
1739 table_name = table_name
1740 ))
1741 .expect("Failed to create trigger");
1742 0 },
1744 )
1745 }
1746
1747 fn establish_inner(database_url: &str) -> Result<SqliteConnection, ConnectionError> {
1748 use crate::result::ConnectionError::CouldntSetupConfiguration;
1749 let raw_connection = RawConnection::establish(database_url)?;
1750 let conn = Self {
1751 statement_cache: StatementCache::new(),
1752 raw_connection,
1753 transaction_state: AnsiTransactionManager::default(),
1754 metadata_lookup: (),
1755 instrumentation: DynInstrumentation::none(),
1756 serialized_data: Vec::new(),
1757 };
1758 conn.register_diesel_sql_functions()
1759 .map_err(CouldntSetupConfiguration)?;
1760 Ok(conn)
1761 }
1762}
1763
1764fn error_message(err_code: libc::c_int) -> &'static str {
1765 ffi::code_to_str(err_code)
1766}
1767
1768#[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)]
1769struct AttachDatabase<'a> {
1770 path: &'a str,
1771 schema_name: &'a str,
1772}
1773
1774impl QueryFragment<Sqlite> for AttachDatabase<'_> {
1775 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1776 out.push_sql("ATTACH DATABASE ");
1777 out.push_bind_param::<crate::sql_types::Text, _>(self.path)?;
1778 out.push_sql(" AS ");
1779 out.push_bind_param::<crate::sql_types::Text, _>(self.schema_name)?;
1780 Ok(())
1781 }
1782}
1783
1784impl RunQueryDslSupport for AttachDatabase<'_> {}
1785
1786#[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)]
1787struct DetachDatabase<'a> {
1788 schema_name: &'a str,
1789}
1790
1791impl QueryFragment<Sqlite> for DetachDatabase<'_> {
1792 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1793 out.push_sql("DETACH DATABASE ");
1794 out.push_bind_param::<crate::sql_types::Text, _>(self.schema_name)?;
1795 Ok(())
1796 }
1797}
1798
1799impl RunQueryDslSupport for DetachDatabase<'_> {}
1800
1801struct Pragma<'a, ST> {
1805 schema: Option<&'a str>,
1806 name: &'static str,
1807 sql_type: PhantomData<ST>,
1808}
1809
1810impl<'a, ST> Pragma<'a, ST> {
1811 fn new(name: &'static str, schema: Option<&'a str>) -> Self {
1812 Pragma {
1813 schema,
1814 name,
1815 sql_type: PhantomData,
1816 }
1817 }
1818}
1819
1820impl<ST> QueryFragment<Sqlite> for Pragma<'_, ST> {
1821 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1822 out.push_sql("PRAGMA ");
1823 out.push_identifier(self.schema.unwrap_or("main"))?;
1824 out.push_sql(".");
1825 out.push_sql(self.name);
1826 Ok(())
1827 }
1828}
1829
1830impl<ST> QueryId for Pragma<'_, ST> {
1832 type QueryId = ();
1833
1834 const HAS_STATIC_QUERY_ID: bool = false;
1835}
1836
1837impl<ST> Query for Pragma<'_, ST> {
1838 type SqlType = ST;
1839}
1840
1841impl<ST> RunQueryDslSupport for Pragma<'_, ST> {}
1842
1843struct SetPragmaInt<'a> {
1846 schema: Option<&'a str>,
1847 name: &'static str,
1848 value: i32,
1849}
1850
1851impl QueryFragment<Sqlite> for SetPragmaInt<'_> {
1852 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1853 out.push_sql("PRAGMA ");
1854 out.push_identifier(self.schema.unwrap_or("main"))?;
1855 out.push_sql(".");
1856 out.push_sql(self.name);
1857 out.push_sql(" = ");
1858 out.push_sql(&self.value.to_string());
1859 Ok(())
1860 }
1861}
1862
1863impl QueryId for SetPragmaInt<'_> {
1864 type QueryId = ();
1865
1866 const HAS_STATIC_QUERY_ID: bool = false;
1867}
1868
1869impl RunQueryDslSupport for SetPragmaInt<'_> {}
1870
1871struct Vacuum<'a> {
1874 schema: Option<&'a str>,
1875 into: Option<&'a str>,
1876}
1877
1878impl QueryFragment<Sqlite> for Vacuum<'_> {
1879 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1880 out.push_sql("VACUUM ");
1881 out.push_identifier(self.schema.unwrap_or("main"))?;
1882 if let Some(into) = self.into {
1883 out.push_sql(" INTO ");
1884 out.push_bind_param::<crate::sql_types::Text, _>(into)?;
1885 }
1886 Ok(())
1887 }
1888}
1889
1890impl QueryId for Vacuum<'_> {
1892 type QueryId = ();
1893
1894 const HAS_STATIC_QUERY_ID: bool = false;
1895}
1896
1897impl RunQueryDslSupport for Vacuum<'_> {}
1898
1899struct WalCheckpoint<'a> {
1906 schema: Option<&'a str>,
1907 mode: WalCheckpointMode,
1908}
1909
1910impl QueryFragment<Sqlite> for WalCheckpoint<'_> {
1911 fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
1912 out.push_sql("PRAGMA ");
1913 if let Some(schema) = self.schema {
1914 out.push_identifier(schema)?;
1915 out.push_sql(".");
1916 }
1917 out.push_sql(match self.mode {
1918 WalCheckpointMode::Passive => "wal_checkpoint(PASSIVE)",
1919 WalCheckpointMode::Full => "wal_checkpoint(FULL)",
1920 WalCheckpointMode::Restart => "wal_checkpoint(RESTART)",
1921 WalCheckpointMode::Truncate => "wal_checkpoint(TRUNCATE)",
1922 WalCheckpointMode::Noop => "wal_checkpoint(NOOP)",
1923 });
1924 Ok(())
1925 }
1926}
1927
1928impl QueryId for WalCheckpoint<'_> {
1930 type QueryId = ();
1931
1932 const HAS_STATIC_QUERY_ID: bool = false;
1933}
1934
1935impl Query for WalCheckpoint<'_> {
1936 type SqlType = (
1937 crate::sql_types::Integer,
1938 crate::sql_types::BigInt,
1939 crate::sql_types::BigInt,
1940 );
1941}
1942
1943impl RunQueryDslSupport for WalCheckpoint<'_> {}
1944
1945#[cfg(test)]
1946mod tests {
1947 use super::*;
1948 use crate::dsl::sql;
1949 use crate::prelude::*;
1950 use crate::sql_types::{Integer, Text};
1951 use crate::sqlite::SqliteFunctionBehavior;
1952
1953 fn connection() -> SqliteConnection {
1954 SqliteConnection::establish(":memory:").unwrap()
1955 }
1956
1957 #[diesel_test_helper::test]
1958 #[allow(unsafe_code)]
1959 fn with_raw_connection_can_return_values() {
1960 let connection = &mut connection();
1961
1962 let autocommit_status = unsafe {
1964 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1965 };
1966
1967 assert_ne!(autocommit_status, 0, "Expected autocommit to be enabled");
1969 }
1970
1971 #[diesel_test_helper::test]
1972 #[allow(unsafe_code)]
1973 fn with_raw_connection_works_after_diesel_operations() {
1974 let connection = &mut connection();
1975
1976 crate::sql_query("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")
1978 .execute(connection)
1979 .unwrap();
1980 crate::sql_query("INSERT INTO test_table (value) VALUES ('hello')")
1981 .execute(connection)
1982 .unwrap();
1983
1984 let last_rowid = unsafe {
1986 connection.with_raw_connection(|raw_conn| ffi::sqlite3_last_insert_rowid(raw_conn))
1987 };
1988
1989 assert_eq!(last_rowid, 1, "Last insert rowid should be 1");
1990
1991 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM test_table")
1993 .get_result(connection)
1994 .unwrap();
1995 assert_eq!(count, 1);
1996 }
1997
1998 #[diesel_test_helper::test]
1999 #[allow(unsafe_code)]
2000 fn with_raw_connection_can_execute_raw_sql() {
2001 let connection = &mut connection();
2002
2003 crate::sql_query("CREATE TABLE raw_test (id INTEGER PRIMARY KEY, name TEXT)")
2005 .execute(connection)
2006 .unwrap();
2007
2008 let result = unsafe {
2011 connection.with_raw_connection(|raw_conn| {
2012 let sql = c"INSERT INTO raw_test (name) VALUES ('from_raw')";
2013 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
2014 let rc = ffi::sqlite3_exec(
2015 raw_conn,
2016 sql.as_ptr(),
2017 None,
2018 core::ptr::null_mut(),
2019 &mut err_msg,
2020 );
2021 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
2022 ffi::sqlite3_free(err_msg as *mut libc::c_void);
2023 }
2024 rc
2025 })
2026 };
2027
2028 assert_eq!(result, ffi::SQLITE_OK, "Raw SQL execution should succeed");
2029
2030 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM raw_test")
2032 .get_result(connection)
2033 .unwrap();
2034 assert_eq!(count, 1);
2035
2036 let name: String = sql::<Text>("SELECT name FROM raw_test WHERE id = 1")
2037 .get_result(connection)
2038 .unwrap();
2039 assert_eq!(name, "from_raw");
2040 }
2041
2042 #[diesel_test_helper::test]
2043 #[allow(unsafe_code)]
2044 fn with_raw_connection_works_within_transaction() {
2045 let connection = &mut connection();
2046
2047 crate::sql_query("CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value INTEGER)")
2048 .execute(connection)
2049 .unwrap();
2050
2051 connection
2052 .transaction::<_, crate::result::Error, _>(|conn| {
2053 crate::sql_query("INSERT INTO txn_test (value) VALUES (42)")
2054 .execute(conn)
2055 .unwrap();
2056
2057 let autocommit = unsafe {
2059 conn.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
2060 };
2061
2062 assert_eq!(
2064 autocommit, 0,
2065 "Autocommit should be disabled inside transaction"
2066 );
2067
2068 Ok(())
2069 })
2070 .unwrap();
2071
2072 let autocommit = unsafe {
2074 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
2075 };
2076 assert_ne!(
2077 autocommit, 0,
2078 "Autocommit should be enabled after transaction"
2079 );
2080 }
2081
2082 #[diesel_test_helper::test]
2083 #[allow(unsafe_code)]
2084 fn with_raw_connection_can_read_database_filename() {
2085 let connection = &mut connection();
2086
2087 let filename = unsafe {
2089 connection.with_raw_connection(|raw_conn| {
2090 let db_name = c"main";
2091 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
2092 if filename_ptr.is_null() {
2093 None
2094 } else {
2095 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
2097 Some(cstr.to_string_lossy().into_owned())
2098 }
2099 })
2100 };
2101
2102 assert_eq!(
2105 filename,
2106 Some(String::new()),
2107 "In-memory database filename should be an empty string"
2108 );
2109 }
2110
2111 #[diesel_test_helper::test]
2112 #[allow(unsafe_code)]
2113 fn with_raw_connection_changes_count() {
2114 let connection = &mut connection();
2115
2116 crate::sql_query("CREATE TABLE changes_test (id INTEGER PRIMARY KEY, value INTEGER)")
2117 .execute(connection)
2118 .unwrap();
2119
2120 crate::sql_query("INSERT INTO changes_test (value) VALUES (1), (2), (3)")
2121 .execute(connection)
2122 .unwrap();
2123
2124 let changes = unsafe {
2126 connection.with_raw_connection(|raw_conn| {
2127 let sql = c"UPDATE changes_test SET value = value + 10";
2128 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
2129 let rc = ffi::sqlite3_exec(
2130 raw_conn,
2131 sql.as_ptr(),
2132 None,
2133 core::ptr::null_mut(),
2134 &mut err_msg,
2135 );
2136 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
2137 ffi::sqlite3_free(err_msg as *mut libc::c_void);
2138 return -1;
2139 }
2140 ffi::sqlite3_changes(raw_conn)
2141 })
2142 };
2143
2144 assert_eq!(changes, 3, "Should have updated 3 rows");
2145
2146 let values: Vec<i32> = sql::<Integer>("SELECT value FROM changes_test ORDER BY id")
2148 .load(connection)
2149 .unwrap();
2150 assert_eq!(values, vec![11, 12, 13]);
2151 }
2152
2153 #[diesel_test_helper::test]
2155 #[allow(unsafe_code)]
2156 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2157 fn with_raw_connection_recovers_after_panic() {
2158 let connection = &mut connection();
2159
2160 crate::sql_query("CREATE TABLE panic_test (id INTEGER PRIMARY KEY, value TEXT)")
2161 .execute(connection)
2162 .unwrap();
2163
2164 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
2166 connection.with_raw_connection(|_raw_conn| {
2167 panic!("intentional panic inside with_raw_connection");
2168 })
2169 }));
2170 assert!(result.is_err(), "Should have caught the panic");
2171
2172 crate::sql_query("INSERT INTO panic_test (value) VALUES ('after_panic')")
2174 .execute(connection)
2175 .unwrap();
2176
2177 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM panic_test")
2178 .get_result(connection)
2179 .unwrap();
2180 assert_eq!(count, 1, "Connection should work after panic in callback");
2181 }
2182
2183 #[diesel_test_helper::test]
2185 #[allow(unsafe_code)]
2186 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
2187 fn with_raw_connection_can_read_file_database_filename() {
2188 let dir = std::env::temp_dir().join("diesel_test_filename.db");
2189 let db_path = dir.to_str().unwrap();
2190
2191 let _ = std::fs::remove_file(db_path);
2193
2194 let connection = &mut SqliteConnection::establish(db_path).unwrap();
2195
2196 let filename = unsafe {
2198 connection.with_raw_connection(|raw_conn| {
2199 let db_name = c"main";
2200 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
2201 if filename_ptr.is_null() {
2202 None
2203 } else {
2204 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
2205 Some(cstr.to_string_lossy().into_owned())
2206 }
2207 })
2208 };
2209
2210 let filename = filename.expect("File-based database should have a filename");
2211 assert!(
2212 filename.contains("diesel_test_filename.db"),
2213 "Filename should contain the database name, got: {filename}"
2214 );
2215
2216 let _ = std::fs::remove_file(db_path);
2218 }
2219
2220 #[declare_sql_function]
2221 extern "SQL" {
2222 fn fun_case(x: Text) -> Text;
2223 fn my_add(x: Integer, y: Integer) -> Integer;
2224 fn answer() -> Integer;
2225 fn add_counter(x: Integer) -> Integer;
2226
2227 #[aggregate]
2228 fn my_sum(expr: Integer) -> Integer;
2229 #[aggregate]
2230 fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable<Integer>;
2231 }
2232
2233 #[diesel_test_helper::test]
2234 fn database_serializes_and_deserializes_successfully() {
2235 let expected_users = vec![
2236 (
2237 1,
2238 "John Doe".to_string(),
2239 "john.doe@example.com".to_string(),
2240 ),
2241 (
2242 2,
2243 "Jane Doe".to_string(),
2244 "jane.doe@example.com".to_string(),
2245 ),
2246 ];
2247
2248 let conn1 = &mut connection();
2249 let _ =
2250 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
2251 .execute(conn1);
2252 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
2253 .execute(conn1);
2254
2255 for _i in 0..2 {
2256 let serialized_database = conn1.serialize_database_to_buffer();
2257 let conn2 = &mut connection();
2258 conn2
2259 .deserialize_readonly_database_from_buffer(serialized_database.as_slice())
2260 .unwrap();
2261
2262 let query =
2263 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
2264 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
2265
2266 assert_eq!(expected_users, actual_users);
2267 std::mem::drop(serialized_database);
2271 let query =
2272 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
2273 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
2274
2275 assert_eq!(expected_users, actual_users);
2276 }
2277 }
2278
2279 #[diesel_test_helper::test]
2280 fn database_deserialize_random_bytes() {
2281 let buffer = vec![0, 1, 2, 3, 4];
2282 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
2283
2284 conn.deserialize_readonly_database_from_buffer(&buffer)
2285 .unwrap();
2286
2287 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
2288
2289 assert!(r.is_err());
2290 assert_eq!(r.unwrap_err().to_string(), "file is not a database");
2291
2292 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
2293
2294 let _ =
2295 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
2296 .execute(conn);
2297 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
2298 .execute(conn);
2299
2300 let db = conn.serialize_database_to_buffer();
2301 let mut bad_buffer = db[..100].to_vec();
2303 bad_buffer.extend(b"whatever");
2304 conn.deserialize_readonly_database_from_buffer(&bad_buffer)
2305 .unwrap();
2306
2307 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
2308
2309 assert!(r.is_err());
2310 assert_eq!(
2311 r.unwrap_err().to_string(),
2312 "database disk image is malformed"
2313 );
2314
2315 let mut size_fitting_bad_buffer = db[..100].to_vec();
2317 size_fitting_bad_buffer.extend(
2318 core::iter::repeat(b"abcdefghij")
2319 .flatten()
2320 .take(db.len() - 100),
2321 );
2322 let r = conn.deserialize_readonly_database_from_buffer(&size_fitting_bad_buffer);
2323
2324 assert!(r.is_err());
2325 assert_eq!(
2326 r.unwrap_err().to_string(),
2327 "database disk image is malformed"
2328 );
2329 }
2330
2331 #[diesel_test_helper::test]
2332 fn register_custom_function() {
2333 let connection = &mut connection();
2334 fun_case_utils::register_impl(connection, |x: String| {
2335 x.chars()
2336 .enumerate()
2337 .map(|(i, c)| {
2338 if i % 2 == 0 {
2339 c.to_lowercase().to_string()
2340 } else {
2341 c.to_uppercase().to_string()
2342 }
2343 })
2344 .collect::<String>()
2345 })
2346 .unwrap();
2347
2348 let mapped_string = crate::select(fun_case("foobar"))
2349 .get_result::<String>(connection)
2350 .unwrap();
2351 assert_eq!("fOoBaR", mapped_string);
2352 }
2353
2354 #[diesel_test_helper::test]
2355 fn register_multiarg_function() {
2356 let connection = &mut connection();
2357 my_add_utils::register_impl(connection, |x: i32, y: i32| x + y).unwrap();
2358
2359 let added = crate::select(my_add(1, 2)).get_result::<i32>(connection);
2360 assert_eq!(Ok(3), added);
2361 }
2362
2363 #[diesel_test_helper::test]
2364 fn register_noarg_function() {
2365 let connection = &mut connection();
2366 answer_utils::register_impl(connection, || 42).unwrap();
2367
2368 let answer = crate::select(answer()).get_result::<i32>(connection);
2369 assert_eq!(Ok(42), answer);
2370 }
2371
2372 #[diesel_test_helper::test]
2373 fn register_nondeterministic_noarg_function() {
2374 let connection = &mut connection();
2375 answer_utils::register_nondeterministic_impl(connection, || 42).unwrap();
2376
2377 let answer = crate::select(answer()).get_result::<i32>(connection);
2378 assert_eq!(Ok(42), answer);
2379 }
2380
2381 #[diesel_test_helper::test]
2382 fn register_nondeterministic_function() {
2383 let connection = &mut connection();
2384 let mut y = 0;
2385 add_counter_utils::register_nondeterministic_impl(connection, move |x: i32| {
2386 y += 1;
2387 x + y
2388 })
2389 .unwrap();
2390
2391 let added = crate::select((add_counter(1), add_counter(1), add_counter(1)))
2392 .get_result::<(i32, i32, i32)>(connection);
2393 assert_eq!(Ok((2, 3, 4)), added);
2394 }
2395
2396 #[derive(Default)]
2397 struct MySum {
2398 sum: i32,
2399 }
2400
2401 impl SqliteAggregateFunction<i32> for MySum {
2402 type Output = i32;
2403
2404 fn step(&mut self, expr: i32) {
2405 self.sum += expr;
2406 }
2407
2408 fn finalize(aggregator: Option<Self>) -> Self::Output {
2409 aggregator.map(|a| a.sum).unwrap_or_default()
2410 }
2411 }
2412
2413 table! {
2414 my_sum_example {
2415 id -> Integer,
2416 value -> Integer,
2417 }
2418 }
2419
2420 #[diesel_test_helper::test]
2421 fn register_aggregate_function() {
2422 use self::my_sum_example::dsl::*;
2423
2424 let connection = &mut connection();
2425 crate::sql_query(
2426 "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
2427 )
2428 .execute(connection)
2429 .unwrap();
2430 crate::sql_query("INSERT INTO my_sum_example (value) VALUES (1), (2), (3)")
2431 .execute(connection)
2432 .unwrap();
2433
2434 my_sum_utils::register_impl_with_behavior::<MySum, _>(
2435 connection,
2436 SqliteFunctionBehavior::DETERMINISTIC,
2437 )
2438 .unwrap();
2439
2440 let result = my_sum_example
2441 .select(my_sum(value))
2442 .get_result::<i32>(connection);
2443 assert_eq!(Ok(6), result);
2444 }
2445
2446 #[diesel_test_helper::test]
2447 fn register_aggregate_function_returns_finalize_default_on_empty_set() {
2448 use self::my_sum_example::dsl::*;
2449
2450 let connection = &mut connection();
2451 crate::sql_query(
2452 "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
2453 )
2454 .execute(connection)
2455 .unwrap();
2456
2457 my_sum_utils::register_impl_with_behavior::<MySum, _>(
2458 connection,
2459 SqliteFunctionBehavior::DETERMINISTIC,
2460 )
2461 .unwrap();
2462
2463 let result = my_sum_example
2464 .select(my_sum(value))
2465 .get_result::<i32>(connection);
2466 assert_eq!(Ok(0), result);
2467 }
2468
2469 #[derive(Default)]
2470 struct RangeMax<T> {
2471 max_value: Option<T>,
2472 }
2473
2474 impl<T: Default + Ord + Copy + Clone> SqliteAggregateFunction<(T, T, T)> for RangeMax<T> {
2475 type Output = Option<T>;
2476
2477 fn step(&mut self, (x0, x1, x2): (T, T, T)) {
2478 let max = if x0 >= x1 && x0 >= x2 {
2479 x0
2480 } else if x1 >= x0 && x1 >= x2 {
2481 x1
2482 } else {
2483 x2
2484 };
2485
2486 self.max_value = match self.max_value {
2487 Some(current_max_value) if max > current_max_value => Some(max),
2488 None => Some(max),
2489 _ => self.max_value,
2490 };
2491 }
2492
2493 fn finalize(aggregator: Option<Self>) -> Self::Output {
2494 aggregator?.max_value
2495 }
2496 }
2497
2498 table! {
2499 range_max_example {
2500 id -> Integer,
2501 value1 -> Integer,
2502 value2 -> Integer,
2503 value3 -> Integer,
2504 }
2505 }
2506
2507 #[diesel_test_helper::test]
2508 fn register_aggregate_multiarg_function() {
2509 use self::range_max_example::dsl::*;
2510
2511 let connection = &mut connection();
2512 crate::sql_query(
2513 r#"CREATE TABLE range_max_example (
2514 id integer primary key autoincrement,
2515 value1 integer,
2516 value2 integer,
2517 value3 integer
2518 )"#,
2519 )
2520 .execute(connection)
2521 .unwrap();
2522 crate::sql_query(
2523 "INSERT INTO range_max_example (value1, value2, value3) VALUES (3, 2, 1), (2, 2, 2)",
2524 )
2525 .execute(connection)
2526 .unwrap();
2527
2528 range_max_utils::register_impl_with_behavior::<RangeMax<i32>, _, _, _>(
2529 connection,
2530 SqliteFunctionBehavior::DETERMINISTIC,
2531 )
2532 .unwrap();
2533 let result = range_max_example
2534 .select(range_max(value1, value2, value3))
2535 .get_result::<Option<i32>>(connection)
2536 .unwrap();
2537 assert_eq!(Some(3), result);
2538 }
2539
2540 table! {
2541 my_collation_example {
2542 id -> Integer,
2543 value -> Text,
2544 }
2545 }
2546
2547 #[diesel_test_helper::test]
2548 fn register_collation_function() {
2549 use self::my_collation_example::dsl::*;
2550
2551 let connection = &mut connection();
2552
2553 connection
2554 .register_collation("RUSTNOCASE", |rhs, lhs| {
2555 rhs.to_lowercase().cmp(&lhs.to_lowercase())
2556 })
2557 .unwrap();
2558
2559 crate::sql_query(
2560 "CREATE TABLE my_collation_example (id integer primary key autoincrement, value text collate RUSTNOCASE)",
2561 ).execute(connection)
2562 .unwrap();
2563 crate::sql_query(
2564 "INSERT INTO my_collation_example (value) VALUES ('foo'), ('FOo'), ('f00')",
2565 )
2566 .execute(connection)
2567 .unwrap();
2568
2569 let result = my_collation_example
2570 .filter(value.eq("foo"))
2571 .select(value)
2572 .load::<String>(connection);
2573 assert_eq!(
2574 Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
2575 result.as_ref().map(|vec| vec.as_ref())
2576 );
2577
2578 let result = my_collation_example
2579 .filter(value.eq("FOO"))
2580 .select(value)
2581 .load::<String>(connection);
2582 assert_eq!(
2583 Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
2584 result.as_ref().map(|vec| vec.as_ref())
2585 );
2586
2587 let result = my_collation_example
2588 .filter(value.eq("f00"))
2589 .select(value)
2590 .load::<String>(connection);
2591 assert_eq!(
2592 Ok(&["f00".to_owned()][..]),
2593 result.as_ref().map(|vec| vec.as_ref())
2594 );
2595
2596 let result = my_collation_example
2597 .filter(value.eq("F00"))
2598 .select(value)
2599 .load::<String>(connection);
2600 assert_eq!(
2601 Ok(&["f00".to_owned()][..]),
2602 result.as_ref().map(|vec| vec.as_ref())
2603 );
2604
2605 let result = my_collation_example
2606 .filter(value.eq("oof"))
2607 .select(value)
2608 .load::<String>(connection);
2609 assert_eq!(Ok(&[][..]), result.as_ref().map(|vec| vec.as_ref()));
2610 }
2611
2612 #[diesel_test_helper::test]
2614 fn test_correct_serialization_of_owned_strings() {
2615 use crate::prelude::*;
2616
2617 #[derive(Debug, crate::expression::AsExpression)]
2618 #[diesel(sql_type = diesel::sql_types::Text)]
2619 struct CustomWrapper(String);
2620
2621 impl crate::serialize::ToSql<Text, Sqlite> for CustomWrapper {
2622 fn to_sql<'b>(
2623 &'b self,
2624 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
2625 ) -> crate::serialize::Result {
2626 out.set_value(self.0.to_string());
2627 Ok(crate::serialize::IsNull::No)
2628 }
2629 }
2630
2631 let connection = &mut connection();
2632
2633 let res = crate::select(
2634 CustomWrapper("".into())
2635 .into_sql::<crate::sql_types::Text>()
2636 .nullable(),
2637 )
2638 .get_result::<Option<String>>(connection)
2639 .unwrap();
2640 assert_eq!(res, Some(String::new()));
2641 }
2642
2643 #[diesel_test_helper::test]
2644 fn test_correct_serialization_of_owned_bytes() {
2645 use crate::prelude::*;
2646
2647 #[derive(Debug, crate::expression::AsExpression)]
2648 #[diesel(sql_type = diesel::sql_types::Binary)]
2649 struct CustomWrapper(Vec<u8>);
2650
2651 impl crate::serialize::ToSql<crate::sql_types::Binary, Sqlite> for CustomWrapper {
2652 fn to_sql<'b>(
2653 &'b self,
2654 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
2655 ) -> crate::serialize::Result {
2656 out.set_value(self.0.clone());
2657 Ok(crate::serialize::IsNull::No)
2658 }
2659 }
2660
2661 let connection = &mut connection();
2662
2663 let res = crate::select(
2664 CustomWrapper(Vec::new())
2665 .into_sql::<crate::sql_types::Binary>()
2666 .nullable(),
2667 )
2668 .get_result::<Option<Vec<u8>>>(connection)
2669 .unwrap();
2670 assert_eq!(res, Some(Vec::new()));
2671 }
2672
2673 #[diesel_test_helper::test]
2674 fn correctly_handle_empty_query() {
2675 let check_empty_query_error = |r: crate::QueryResult<usize>| {
2676 assert!(r.is_err());
2677 let err = r.unwrap_err();
2678 assert!(
2679 matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::<crate::result::EmptyQuery>()),
2680 "Expected a query builder error, but got {err}"
2681 );
2682 };
2683 let connection = &mut SqliteConnection::establish(":memory:").unwrap();
2684 check_empty_query_error(crate::sql_query("").execute(connection));
2685 check_empty_query_error(crate::sql_query(" ").execute(connection));
2686 check_empty_query_error(crate::sql_query("\n\t").execute(connection));
2687 check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection));
2688 }
2689
2690 #[diesel_test_helper::test]
2691 fn last_insert_rowid_returns_none_on_fresh_connection() {
2692 let conn = &mut connection();
2693 assert_eq!(conn.last_insert_rowid(), None);
2694 }
2695
2696 #[diesel_test_helper::test]
2697 fn last_insert_rowid_returns_rowid_after_insert() {
2698 let conn = &mut connection();
2699 crate::sql_query("CREATE TABLE li_test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2700 .execute(conn)
2701 .unwrap();
2702
2703 crate::sql_query("INSERT INTO li_test (val) VALUES ('a')")
2704 .execute(conn)
2705 .unwrap();
2706 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2707
2708 crate::sql_query("INSERT INTO li_test (val) VALUES ('b')")
2709 .execute(conn)
2710 .unwrap();
2711 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(2));
2712 }
2713
2714 #[diesel_test_helper::test]
2715 fn last_insert_rowid_unchanged_after_failed_insert() {
2716 let conn = &mut connection();
2717 crate::sql_query(
2718 "CREATE TABLE li_test2 (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)",
2719 )
2720 .execute(conn)
2721 .unwrap();
2722
2723 crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')")
2724 .execute(conn)
2725 .unwrap();
2726 let rowid = conn.last_insert_rowid();
2727 assert_eq!(rowid, NonZeroI64::new(1));
2728
2729 let result = crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')").execute(conn);
2731 assert!(result.is_err());
2732
2733 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2735 }
2736
2737 #[diesel_test_helper::test]
2738 fn last_insert_rowid_with_explicit_rowid() {
2739 let conn = &mut connection();
2740 crate::sql_query("CREATE TABLE li_test3 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2741 .execute(conn)
2742 .unwrap();
2743
2744 crate::sql_query("INSERT INTO li_test3 (id, val) VALUES (42, 'a')")
2745 .execute(conn)
2746 .unwrap();
2747 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(42));
2748 }
2749
2750 #[diesel_test_helper::test]
2751 fn last_insert_rowid_unchanged_after_delete_and_update() {
2752 let conn = &mut connection();
2753 crate::sql_query("CREATE TABLE li_test4 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2754 .execute(conn)
2755 .unwrap();
2756
2757 crate::sql_query("INSERT INTO li_test4 (val) VALUES ('a')")
2758 .execute(conn)
2759 .unwrap();
2760 let rowid = conn.last_insert_rowid();
2761 assert_eq!(rowid, NonZeroI64::new(1));
2762
2763 crate::sql_query("UPDATE li_test4 SET val = 'b' WHERE id = 1")
2764 .execute(conn)
2765 .unwrap();
2766 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2767
2768 crate::sql_query("DELETE FROM li_test4 WHERE id = 1")
2769 .execute(conn)
2770 .unwrap();
2771 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2772 }
2773
2774 #[diesel_test_helper::test]
2775 fn read_bytes_from_blob() {
2776 table! {
2777 blobs {
2778 id -> Integer,
2779 data -> Blob,
2780 data2 -> Blob,
2781 }
2782 }
2783
2784 use std::io::Read;
2785
2786 let conn = &mut connection();
2787
2788 let _ =
2789 crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB, data2 BLOB)")
2790 .execute(conn);
2791
2792 let _ = crate::sql_query(
2793 "INSERT INTO blobs (data, data2) VALUES ('abc', 'def'), ('123', '456')",
2794 )
2795 .execute(conn);
2796
2797 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2798 let mut buf = vec![];
2799 data.read_to_end(&mut buf).unwrap();
2800
2801 assert_eq!(buf, b"abc");
2802
2803 let mut data2 = conn.get_read_only_blob(blobs::data2, 1).unwrap();
2804 let mut buf = vec![];
2805 data2.read_to_end(&mut buf).unwrap();
2806
2807 assert_eq!(buf, b"def");
2808 }
2809
2810 #[diesel_test_helper::test]
2811 fn read_seek_bytes() {
2812 table! {
2813 blobs {
2814 id -> Integer,
2815 data -> Blob,
2816 }
2817 }
2818
2819 use std::io::Read;
2820 use std::io::Seek;
2821 use std::io::SeekFrom;
2822
2823 let conn = &mut connection();
2824
2825 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2826 .execute(conn);
2827
2828 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abcdefghi')").execute(conn);
2829
2830 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2831
2832 let mut buf = [0; 1];
2833 assert_eq!(data.read(&mut buf).unwrap(), 1);
2834 assert_eq!(&buf, b"a");
2835
2836 assert_eq!(data.seek(SeekFrom::Current(1)).unwrap(), 2);
2838
2839 let mut buf = [0; 1];
2840 assert_eq!(data.read(&mut buf).unwrap(), 1);
2841 assert_eq!(&buf, b"c");
2842
2843 assert_eq!(data.seek(SeekFrom::Start(0)).unwrap(), 0);
2845
2846 let mut buf = [0; 1];
2847 assert_eq!(data.read(&mut buf).unwrap(), 1);
2848 assert_eq!(&buf, b"a");
2849
2850 assert_eq!(data.seek(SeekFrom::Current(-10)).unwrap(), 0);
2852
2853 let mut buf = [0; 1];
2854 assert_eq!(data.read(&mut buf).unwrap(), 1);
2855 assert_eq!(&buf, b"a");
2856
2857 data.seek(SeekFrom::Current(100)).unwrap();
2859
2860 let mut buf = [0; 1];
2862 assert_eq!(data.read(&mut buf).unwrap(), 0);
2863 }
2864
2865 #[diesel_test_helper::test]
2866 fn use_conn_after_blob_drop() {
2867 table! {
2868 blobs {
2869 id -> Integer,
2870 data -> Blob,
2871 }
2872 }
2873
2874 let conn = &mut connection();
2875
2876 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2877 .execute(conn);
2878
2879 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abc')").execute(conn);
2880
2881 let data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2882 drop(data);
2883
2884 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('def')").execute(conn);
2885 }
2886
2887 #[diesel_test_helper::test]
2888 fn blob_transaction() {
2889 table! {
2890 blobs {
2891 id -> Integer,
2892 data -> Blob,
2893 }
2894 }
2895
2896 use std::io::Read;
2897
2898 let conn = &mut connection();
2899
2900 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2901 .execute(conn);
2902
2903 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abc')").execute(conn);
2904
2905 {
2906 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2907 let mut buf = vec![];
2908 data.read_to_end(&mut buf).unwrap();
2909 assert_eq!(buf, b"abc");
2910 }
2911
2912 let res = conn.exclusive_transaction(|conn| {
2913 crate::sql_query("UPDATE blobs SET data = 'def' WHERE id = 1").execute(conn)?;
2914
2915 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2916 let mut buf = vec![];
2917 data.read_to_end(&mut buf).unwrap();
2918 assert_eq!(buf, b"def");
2919
2920 Result::<(), _>::Err(Error::RollbackTransaction)
2921 });
2922
2923 assert_eq!(res.unwrap_err(), Error::RollbackTransaction);
2924
2925 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2926 let mut buf = vec![];
2927 data.read_to_end(&mut buf).unwrap();
2928 assert_eq!(buf, b"abc");
2929 }
2930
2931 #[diesel_test_helper::test]
2932 fn aggregate_function_works_with_aligned_data() {
2933 #[derive(Debug, Default)]
2934 #[repr(align(64))]
2935 struct OverAligned;
2936
2937 impl SqliteAggregateFunction<i32> for OverAligned {
2938 type Output = i64;
2939
2940 fn step(&mut self, _value: i32) {
2941 let need = core::mem::align_of::<Self>();
2942 let got = core::mem::align_of_val(self);
2943 assert_eq!(need, got);
2944 }
2945
2946 fn finalize(_agg: Option<Self>) -> i64 {
2947 0
2948 }
2949 }
2950 #[declare_sql_function]
2951 extern "SQL" {
2952 #[aggregate]
2953 fn over_aligned_sum(x: Integer) -> diesel::sql_types::BigInt;
2954 }
2955
2956 let mut conn = SqliteConnection::establish(":memory:").unwrap();
2957 over_aligned_sum_utils::register_impl::<OverAligned, _>(&mut conn).unwrap();
2958
2959 diesel::select(over_aligned_sum(1))
2960 .execute(&mut conn)
2961 .unwrap();
2962 }
2963
2964 #[diesel_test_helper::test]
2965 fn sum_twice() {
2966 #[derive(Default)]
2967 struct Sum(i32);
2968
2969 impl SqliteAggregateFunction<i32> for Sum {
2970 type Output = i32;
2971
2972 fn step(&mut self, value: i32) {
2973 self.0 += value;
2974 }
2975
2976 fn finalize(agg: Option<Self>) -> i32 {
2977 agg.map(|s| s.0).unwrap_or_default()
2978 }
2979 }
2980
2981 #[declare_sql_function]
2982 extern "SQL" {
2983 #[aggregate]
2984 fn my_sum(x: Integer) -> Integer;
2985 }
2986
2987 let mut conn = SqliteConnection::establish(":memory:").unwrap();
2988 my_sum_utils::register_impl::<Sum, _>(&mut conn).unwrap();
2989
2990 conn.batch_execute(
2991 "
2992 CREATE TABLE test(key1 INTEGER, key2 INTEGER);
2993 INSERT INTO test(key1, key2) VALUES (1, 2), (2, 4), (3, 6);
2994",
2995 )
2996 .unwrap();
2997
2998 table! {
2999 test (key1, key2) {
3000 key1 -> Integer,
3001 key2 -> Integer,
3002 }
3003 }
3004
3005 let (first_res, second_res) = test::table
3006 .select((my_sum(test::key1), my_sum(test::key2)))
3007 .get_result::<(i32, i32)>(&mut conn)
3008 .unwrap();
3009
3010 assert_eq!(first_res, 6);
3011 assert_eq!(second_res, 12);
3012
3013 conn.batch_execute("DELETE FROM test").unwrap();
3014 let (first_res, second_res) = test::table
3015 .select((my_sum(test::key1), my_sum(test::key2)))
3016 .get_result::<(i32, i32)>(&mut conn)
3017 .unwrap();
3018
3019 assert_eq!(first_res, 0);
3020 assert_eq!(second_res, 0);
3021 }
3022
3023 #[diesel_test_helper::test]
3024 fn test_injection() {
3025 diesel::table! {
3026 #[sql_name = "quote'table"]
3027 quote_table (id) {
3028 id -> Nullable<Integer>,
3029 name -> Nullable<Text>,
3030 }
3031 }
3032
3033 let mut conn = SqliteConnection::establish(":memory:").unwrap();
3034
3035 conn.batch_execute("CREATE TABLE \"quote'table\" (id INTEGER PRIMARY KEY, name TEXT);")
3036 .unwrap();
3037
3038 diesel::insert_into(quote_table::table)
3039 .values((quote_table::id.eq(1), quote_table::name.eq("Jane")))
3040 .execute(&mut conn)
3041 .unwrap();
3042
3043 let data = quote_table::table
3044 .load::<(Option<i32>, Option<String>)>(&mut conn)
3045 .unwrap();
3046 assert_eq!(data, [(Some(1), Some("Jane".to_owned()))]);
3047 }
3048
3049 #[diesel_test_helper::test]
3050 fn set_limit_returns_previous_value() {
3051 let mut conn = connection();
3052 let original = conn.get_limit(SqliteLimit::SqlLength);
3053
3054 assert_eq!(conn.set_limit(SqliteLimit::SqlLength, 1024), original);
3057 assert_eq!(conn.set_limit(SqliteLimit::SqlLength, 2048), 1024);
3058 assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 2048);
3059 }
3060
3061 #[diesel_test_helper::test]
3062 fn get_limit_does_not_mutate() {
3063 let conn = connection();
3064 let first = conn.get_limit(SqliteLimit::ExprDepth);
3065 assert!(first > 0);
3068 assert_eq!(conn.get_limit(SqliteLimit::ExprDepth), first);
3069 }
3070
3071 #[diesel_test_helper::test]
3072 fn set_limit_enforces_length() {
3073 let mut conn = connection();
3074 conn.set_limit(SqliteLimit::Length, 100);
3075
3076 assert!(
3077 crate::sql_query("SELECT length(randomblob(50))")
3078 .execute(&mut conn)
3079 .is_ok()
3080 );
3081 assert!(
3083 crate::sql_query("SELECT length(randomblob(500))")
3084 .execute(&mut conn)
3085 .is_err()
3086 );
3087 }
3088
3089 #[diesel_test_helper::test]
3090 fn set_limit_enforces_column_count() {
3091 let wide = format!(
3094 "SELECT {}",
3095 (1..=30)
3096 .map(|i| i.to_string())
3097 .collect::<Vec<_>>()
3098 .join(", ")
3099 );
3100
3101 let mut unconstrained = connection();
3102 assert!(crate::sql_query(&wide).execute(&mut unconstrained).is_ok());
3103
3104 let mut conn = connection();
3105 conn.set_limit(SqliteLimit::ColumnCount, 10);
3106 assert!(crate::sql_query(&wide).execute(&mut conn).is_err());
3107 }
3108
3109 #[diesel_test_helper::test]
3110 fn set_limit_enforces_expr_depth() {
3111 let mut conn = connection();
3112 conn.set_limit(SqliteLimit::ExprDepth, 5);
3113
3114 assert!(crate::sql_query("SELECT 1+1").execute(&mut conn).is_ok());
3115 let deep = format!("SELECT {}1", "1+".repeat(40));
3117 assert!(crate::sql_query(&deep).execute(&mut conn).is_err());
3118 }
3119
3120 #[diesel_test_helper::test]
3121 fn set_limit_enforces_compound_select() {
3122 let mut conn = connection();
3123 conn.set_limit(SqliteLimit::CompoundSelect, 2);
3124
3125 assert!(
3126 crate::sql_query("SELECT 1 UNION SELECT 2")
3127 .execute(&mut conn)
3128 .is_ok()
3129 );
3130 assert!(
3132 crate::sql_query(
3133 "SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5"
3134 )
3135 .execute(&mut conn)
3136 .is_err()
3137 );
3138 }
3139
3140 #[diesel_test_helper::test]
3141 fn set_limit_enforces_vdbe_op() {
3142 let heavy = "SELECT count(*) FROM sqlite_master a, sqlite_master b, sqlite_master c";
3145
3146 let mut unconstrained = connection();
3147 assert!(crate::sql_query(heavy).execute(&mut unconstrained).is_ok());
3148
3149 let mut conn = connection();
3150 conn.set_limit(SqliteLimit::VdbeOp, 5);
3151 assert!(crate::sql_query(heavy).execute(&mut conn).is_err());
3152 }
3153
3154 #[diesel_test_helper::test]
3155 fn set_limit_enforces_function_arg() {
3156 let mut conn = connection();
3157 conn.set_limit(SqliteLimit::FunctionArg, 3);
3158
3159 assert!(
3160 crate::sql_query("SELECT max(1, 2, 3)")
3161 .execute(&mut conn)
3162 .is_ok()
3163 );
3164 assert!(
3166 crate::sql_query("SELECT max(1, 2, 3, 4, 5, 6, 7, 8)")
3167 .execute(&mut conn)
3168 .is_err()
3169 );
3170 }
3171
3172 #[diesel_test_helper::test]
3173 fn set_limit_enforces_attached() {
3174 let mut conn = connection();
3175 conn.set_limit(SqliteLimit::Attached, 0);
3176
3177 assert!(
3179 crate::sql_query("ATTACH DATABASE ':memory:' AS aux_db")
3180 .execute(&mut conn)
3181 .is_err()
3182 );
3183 }
3184
3185 #[diesel_test_helper::test]
3186 fn set_limit_enforces_variable_number() {
3187 let mut conn = connection();
3188 conn.set_limit(
3192 SqliteLimit::VariableNumber,
3193 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT,
3194 );
3195 let at_limit = format!("SELECT ?{}", SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT);
3196 let past_limit = format!(
3197 "SELECT ?{}",
3198 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT as i64 + 1
3199 );
3200 assert!(crate::sql_query(&at_limit).execute(&mut conn).is_ok());
3201 assert!(crate::sql_query(&past_limit).execute(&mut conn).is_err());
3202 }
3203
3204 #[diesel_test_helper::test]
3205 fn set_limit_enforces_trigger_depth() {
3206 use crate::connection::SimpleConnection;
3207
3208 let setup = "PRAGMA recursive_triggers = ON;\
3210 CREATE TABLE recur (x INTEGER);\
3211 CREATE TRIGGER recur_tr AFTER INSERT ON recur WHEN NEW.x < 100 \
3212 BEGIN INSERT INTO recur VALUES (NEW.x + 1); END;";
3213
3214 let mut unconstrained = connection();
3216 unconstrained.batch_execute(setup).unwrap();
3217 assert!(
3218 crate::sql_query("INSERT INTO recur VALUES (1)")
3219 .execute(&mut unconstrained)
3220 .is_ok()
3221 );
3222
3223 let mut conn = connection();
3226 conn.set_limit(SqliteLimit::TriggerDepth, 3);
3227 conn.batch_execute(setup).unwrap();
3228 assert!(
3229 crate::sql_query("INSERT INTO recur VALUES (1)")
3230 .execute(&mut conn)
3231 .is_err()
3232 );
3233 }
3234
3235 #[diesel_test_helper::test]
3236 fn worker_threads_limit_has_no_runtime_error_path() {
3237 let mut conn = connection();
3242 conn.set_limit(SqliteLimit::WorkerThreads, 0);
3243 assert_eq!(conn.get_limit(SqliteLimit::WorkerThreads), 0);
3244 assert!(crate::sql_query("SELECT 1").execute(&mut conn).is_ok());
3245 }
3246
3247 #[diesel_test_helper::test]
3248 fn set_limit_enforces_sql_length() {
3249 let mut conn = connection();
3250 conn.set_limit(SqliteLimit::SqlLength, 20);
3251
3252 let result =
3254 crate::sql_query("SELECT * FROM sqlite_master WHERE type = 'table'").execute(&mut conn);
3255 assert!(result.is_err());
3256 }
3257
3258 #[diesel_test_helper::test]
3259 fn set_limit_enforces_like_pattern_length() {
3260 let mut conn = connection();
3261 conn.set_limit(SqliteLimit::LikePatternLength, 100);
3262
3263 assert!(
3264 crate::sql_query("SELECT 'test' LIKE 'te%'")
3265 .execute(&mut conn)
3266 .is_ok()
3267 );
3268
3269 let long_pattern = "%".repeat(200);
3270 let query = format!("SELECT 'test' LIKE '{long_pattern}'");
3271 assert!(crate::sql_query(&query).execute(&mut conn).is_err());
3272 }
3273
3274 #[diesel_test_helper::test]
3275 fn set_limit_clamps_above_compile_time_maximum() {
3276 let mut conn = connection();
3277 conn.set_limit(SqliteLimit::Length, i32::MAX);
3280 let clamped = conn.get_limit(SqliteLimit::Length);
3281 assert!(clamped > 0 && clamped < i32::MAX);
3282 }
3283
3284 #[diesel_test_helper::test]
3285 fn set_recommended_security_limits_applies_documented_table() {
3286 let mut conn = connection();
3287 conn.set_recommended_security_limits();
3288
3289 assert_eq!(conn.get_limit(SqliteLimit::Length), 1_000_000);
3290 assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 100_000);
3291 assert_eq!(conn.get_limit(SqliteLimit::ColumnCount), 100);
3292 assert_eq!(conn.get_limit(SqliteLimit::ExprDepth), 10);
3293 assert_eq!(conn.get_limit(SqliteLimit::CompoundSelect), 3);
3294 assert_eq!(conn.get_limit(SqliteLimit::VdbeOp), 25_000);
3295 assert_eq!(conn.get_limit(SqliteLimit::FunctionArg), 8);
3296 assert_eq!(conn.get_limit(SqliteLimit::Attached), 0);
3297 assert_eq!(conn.get_limit(SqliteLimit::LikePatternLength), 50);
3298 assert_eq!(conn.get_limit(SqliteLimit::VariableNumber), 10);
3299 assert_eq!(conn.get_limit(SqliteLimit::TriggerDepth), 10);
3300 }
3301
3302 #[diesel_test_helper::test]
3303 fn safe_limit_constants_do_not_exceed_defaults() {
3304 let pairs = [
3310 (
3311 SqliteLimit::SAFE_LENGTH_LIMIT,
3312 SqliteLimit::DEFAULT_LENGTH_LIMIT,
3313 ),
3314 (
3315 SqliteLimit::SAFE_SQL_LENGTH_LIMIT,
3316 SqliteLimit::DEFAULT_SQL_LENGTH_LIMIT,
3317 ),
3318 (
3319 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT,
3320 SqliteLimit::DEFAULT_COLUMN_COUNT_LIMIT,
3321 ),
3322 (
3323 SqliteLimit::SAFE_EXPR_DEPTH_LIMIT,
3324 SqliteLimit::DEFAULT_EXPR_DEPTH_LIMIT,
3325 ),
3326 (
3327 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT,
3328 SqliteLimit::DEFAULT_COMPOUND_SELECT_LIMIT,
3329 ),
3330 (
3331 SqliteLimit::SAFE_VDBE_OP_LIMIT,
3332 SqliteLimit::DEFAULT_VDBE_OP_LIMIT,
3333 ),
3334 (
3335 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT,
3336 SqliteLimit::DEFAULT_FUNCTION_ARG_LIMIT,
3337 ),
3338 (
3339 SqliteLimit::SAFE_ATTACHED_LIMIT,
3340 SqliteLimit::DEFAULT_ATTACHED_LIMIT,
3341 ),
3342 (
3343 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT,
3344 SqliteLimit::DEFAULT_LIKE_PATTERN_LENGTH_LIMIT,
3345 ),
3346 (
3347 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT,
3348 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT,
3349 ),
3350 (
3351 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT,
3352 SqliteLimit::DEFAULT_TRIGGER_DEPTH_LIMIT,
3353 ),
3354 (
3355 SqliteLimit::SAFE_WORKER_THREADS_LIMIT,
3356 SqliteLimit::DEFAULT_WORKER_THREADS_LIMIT,
3357 ),
3358 ];
3359 for (safe, default) in pairs {
3360 assert!(
3361 safe <= default,
3362 "safe value {safe} exceeds default {default}"
3363 );
3364 }
3365 }
3366
3367 #[diesel_test_helper::test]
3368 fn safe_limit_constants_match_recommended_setter() {
3369 let mut conn = connection();
3370 conn.set_recommended_security_limits();
3371
3372 assert_eq!(
3373 conn.get_limit(SqliteLimit::Length),
3374 SqliteLimit::SAFE_LENGTH_LIMIT
3375 );
3376 assert_eq!(
3377 conn.get_limit(SqliteLimit::SqlLength),
3378 SqliteLimit::SAFE_SQL_LENGTH_LIMIT
3379 );
3380 assert_eq!(
3381 conn.get_limit(SqliteLimit::ColumnCount),
3382 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT
3383 );
3384 assert_eq!(
3385 conn.get_limit(SqliteLimit::ExprDepth),
3386 SqliteLimit::SAFE_EXPR_DEPTH_LIMIT
3387 );
3388 assert_eq!(
3389 conn.get_limit(SqliteLimit::CompoundSelect),
3390 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT
3391 );
3392 assert_eq!(
3393 conn.get_limit(SqliteLimit::VdbeOp),
3394 SqliteLimit::SAFE_VDBE_OP_LIMIT
3395 );
3396 assert_eq!(
3397 conn.get_limit(SqliteLimit::FunctionArg),
3398 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT
3399 );
3400 assert_eq!(
3401 conn.get_limit(SqliteLimit::Attached),
3402 SqliteLimit::SAFE_ATTACHED_LIMIT
3403 );
3404 assert_eq!(
3405 conn.get_limit(SqliteLimit::LikePatternLength),
3406 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT
3407 );
3408 assert_eq!(
3409 conn.get_limit(SqliteLimit::VariableNumber),
3410 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT
3411 );
3412 assert_eq!(
3413 conn.get_limit(SqliteLimit::TriggerDepth),
3414 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT
3415 );
3416 assert_eq!(
3419 conn.get_limit(SqliteLimit::WorkerThreads),
3420 SqliteLimit::SAFE_WORKER_THREADS_LIMIT
3421 );
3422 }
3423
3424 #[diesel_test_helper::test]
3427 fn db_config_defensive_roundtrip() {
3428 let conn = &mut connection();
3429 conn.set_defensive(true).unwrap();
3430 assert!(conn.is_defensive().unwrap());
3431 conn.set_defensive(false).unwrap();
3432 assert!(!conn.is_defensive().unwrap());
3433 }
3434
3435 #[diesel_test_helper::test]
3436 fn db_config_trusted_schema_roundtrip() {
3437 let conn = &mut connection();
3438 conn.set_trusted_schema(false).unwrap();
3439 assert!(!conn.is_trusted_schema().unwrap());
3440 conn.set_trusted_schema(true).unwrap();
3441 assert!(conn.is_trusted_schema().unwrap());
3442 }
3443
3444 #[diesel_test_helper::test]
3445 fn db_config_with_load_extension_enabled_scopes_the_flag() {
3446 let conn = &mut connection();
3447 conn.with_load_extension_enabled(|conn| {
3448 assert!(conn.is_load_extension_enabled().unwrap());
3450 QueryResult::Ok(())
3451 })
3452 .unwrap();
3453 assert!(!conn.is_load_extension_enabled().unwrap());
3455 }
3456
3457 #[cfg(all(
3458 feature = "std",
3459 not(all(target_family = "wasm", target_os = "unknown"))
3460 ))]
3461 #[diesel_test_helper::test]
3462 fn with_load_extension_enabled_disables_after_panic() {
3463 let conn = &mut connection();
3464 let outcome = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
3465 conn.with_load_extension_enabled(|_conn| -> QueryResult<()> {
3466 panic!("boom inside closure");
3467 })
3468 }));
3469 assert!(outcome.is_err(), "panic should propagate");
3470 assert!(
3471 !conn.is_load_extension_enabled().unwrap(),
3472 "extension loading must be disabled again after a panic"
3473 );
3474 }
3475
3476 #[diesel_test_helper::test]
3477 fn db_config_triggers_roundtrip() {
3478 let conn = &mut connection();
3479 conn.set_triggers_enabled(false).unwrap();
3480 assert!(!conn.are_triggers_enabled().unwrap());
3481 conn.set_triggers_enabled(true).unwrap();
3482 assert!(conn.are_triggers_enabled().unwrap());
3483 }
3484
3485 #[diesel_test_helper::test]
3486 fn db_config_views_roundtrip() {
3487 let conn = &mut connection();
3488 conn.set_views_enabled(false).unwrap();
3489 assert!(!conn.are_views_enabled().unwrap());
3490 conn.set_views_enabled(true).unwrap();
3491 assert!(conn.are_views_enabled().unwrap());
3492 }
3493
3494 #[diesel_test_helper::test]
3495 fn db_config_foreign_keys_roundtrip() {
3496 let conn = &mut connection();
3497 conn.set_foreign_keys_enabled(true).unwrap();
3498 assert!(conn.are_foreign_keys_enabled().unwrap());
3499 conn.set_foreign_keys_enabled(false).unwrap();
3500 assert!(!conn.are_foreign_keys_enabled().unwrap());
3501 }
3502
3503 #[diesel_test_helper::test]
3504 fn db_config_dqs_dml_roundtrip() {
3505 let conn = &mut connection();
3506 conn.set_double_quoted_strings_dml(false).unwrap();
3507 assert!(!conn.are_double_quoted_strings_dml_enabled().unwrap());
3508 conn.set_double_quoted_strings_dml(true).unwrap();
3509 assert!(conn.are_double_quoted_strings_dml_enabled().unwrap());
3510 }
3511
3512 #[diesel_test_helper::test]
3513 fn db_config_dqs_ddl_roundtrip() {
3514 let conn = &mut connection();
3515 conn.set_double_quoted_strings_ddl(false).unwrap();
3516 assert!(!conn.are_double_quoted_strings_ddl_enabled().unwrap());
3517 conn.set_double_quoted_strings_ddl(true).unwrap();
3518 assert!(conn.are_double_quoted_strings_ddl_enabled().unwrap());
3519 }
3520
3521 #[diesel_test_helper::test]
3522 fn db_config_fts3_tokenizer_roundtrip() {
3523 let conn = &mut connection();
3524 conn.set_fts3_tokenizer_enabled(false).unwrap();
3525 assert!(!conn.is_fts3_tokenizer_enabled().unwrap());
3526 conn.set_fts3_tokenizer_enabled(true).unwrap();
3527 assert!(conn.is_fts3_tokenizer_enabled().unwrap());
3528 }
3529
3530 #[diesel_test_helper::test]
3531 fn db_config_writable_schema_roundtrip() {
3532 let conn = &mut connection();
3533 conn.set_writable_schema(false).unwrap();
3534 assert!(!conn.is_writable_schema().unwrap());
3535 conn.set_writable_schema(true).unwrap();
3536 assert!(conn.is_writable_schema().unwrap());
3537 }
3538
3539 #[diesel_test_helper::test]
3540 fn db_config_attach_create_roundtrip() {
3541 let conn = &mut connection();
3542 if conn.set_attach_create_enabled(false).is_err() {
3544 return;
3545 }
3546 assert!(!conn.is_attach_create_enabled().unwrap());
3547 conn.set_attach_create_enabled(true).unwrap();
3548 assert!(conn.is_attach_create_enabled().unwrap());
3549 }
3550
3551 #[diesel_test_helper::test]
3552 fn db_config_attach_write_roundtrip() {
3553 let conn = &mut connection();
3554 if conn.set_attach_write_enabled(false).is_err() {
3556 return;
3557 }
3558 assert!(!conn.is_attach_write_enabled().unwrap());
3559 conn.set_attach_write_enabled(true).unwrap();
3560 assert!(conn.is_attach_write_enabled().unwrap());
3561 }
3562
3563 #[diesel_test_helper::test]
3566 fn defensive_mode_blocks_writable_schema() {
3567 let conn = &mut connection();
3568 conn.set_defensive(true).unwrap();
3569 let _ = crate::sql_query("PRAGMA writable_schema = ON").execute(conn);
3571 assert!(!conn.is_writable_schema().unwrap());
3572 }
3573
3574 #[diesel_test_helper::test]
3575 fn foreign_keys_enabled_enforces_constraints() {
3576 let conn = &mut connection();
3577 conn.set_foreign_keys_enabled(true).unwrap();
3578
3579 crate::sql_query("CREATE TABLE parent (id INTEGER PRIMARY KEY)")
3580 .execute(conn)
3581 .unwrap();
3582 crate::sql_query(
3583 "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
3584 )
3585 .execute(conn)
3586 .unwrap();
3587
3588 let result =
3590 crate::sql_query("INSERT INTO child (id, parent_id) VALUES (1, 999)").execute(conn);
3591 assert!(result.is_err());
3592 }
3593
3594 #[diesel_test_helper::test]
3595 fn views_disabled_blocks_view_queries() {
3596 let conn = &mut connection();
3597 crate::sql_query("CREATE TABLE base (id INTEGER PRIMARY KEY)")
3598 .execute(conn)
3599 .unwrap();
3600 crate::sql_query("INSERT INTO base (id) VALUES (1)")
3601 .execute(conn)
3602 .unwrap();
3603 crate::sql_query("CREATE VIEW base_view AS SELECT id FROM base")
3604 .execute(conn)
3605 .unwrap();
3606
3607 conn.set_views_enabled(true).unwrap();
3609 assert!(
3610 crate::sql_query("SELECT id FROM base_view")
3611 .execute(conn)
3612 .is_ok()
3613 );
3614
3615 conn.set_views_enabled(false).unwrap();
3617 assert!(
3618 crate::sql_query("SELECT id FROM base_view")
3619 .execute(conn)
3620 .is_err()
3621 );
3622 }
3623
3624 #[diesel_test_helper::test]
3625 fn triggers_disabled_prevents_firing() {
3626 let conn = &mut connection();
3627 crate::sql_query("CREATE TABLE source (id INTEGER PRIMARY KEY)")
3628 .execute(conn)
3629 .unwrap();
3630 crate::sql_query("CREATE TABLE trigger_log (n INTEGER)")
3631 .execute(conn)
3632 .unwrap();
3633 crate::sql_query("CREATE TRIGGER log_insert AFTER INSERT ON source BEGIN INSERT INTO trigger_log (n) VALUES (1); END")
3634 .execute(conn)
3635 .unwrap();
3636
3637 conn.set_triggers_enabled(false).unwrap();
3639 crate::sql_query("INSERT INTO source (id) VALUES (1)")
3640 .execute(conn)
3641 .unwrap();
3642 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
3643 .get_result(conn)
3644 .unwrap();
3645 assert_eq!(0, count, "trigger should not fire while disabled");
3646
3647 conn.set_triggers_enabled(true).unwrap();
3649 crate::sql_query("INSERT INTO source (id) VALUES (2)")
3650 .execute(conn)
3651 .unwrap();
3652 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
3653 .get_result(conn)
3654 .unwrap();
3655 assert_eq!(1, count, "trigger should fire while enabled");
3656 }
3657
3658 #[diesel_test_helper::test]
3659 fn dqs_dml_controls_double_quoted_string_literals() {
3660 let conn = &mut connection();
3661
3662 conn.set_double_quoted_strings_dml(false).unwrap();
3665 let disabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
3666 assert!(disabled.is_err());
3667
3668 conn.set_double_quoted_strings_dml(true).unwrap();
3670 let enabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
3671 assert_eq!(Ok("bare_token".to_owned()), enabled);
3672 }
3673
3674 #[diesel_test_helper::test]
3675 fn dqs_ddl_controls_double_quoted_string_literals() {
3676 let conn = &mut connection();
3677
3678 conn.set_double_quoted_strings_ddl(false).unwrap();
3681 let disabled =
3682 crate::sql_query(r#"CREATE TABLE dqs_off (name TEXT, CHECK (name <> "not_a_column"))"#)
3683 .execute(conn);
3684 assert!(disabled.is_err());
3685
3686 conn.set_double_quoted_strings_ddl(true).unwrap();
3689 let enabled =
3690 crate::sql_query(r#"CREATE TABLE dqs_on (name TEXT, CHECK (name <> "not_a_column"))"#)
3691 .execute(conn);
3692 assert!(enabled.is_ok());
3693 }
3694
3695 #[diesel_test_helper::test]
3696 fn writable_schema_controls_direct_sqlite_master_writes() {
3697 let conn = &mut connection();
3698 crate::sql_query("CREATE TABLE protected (id INTEGER PRIMARY KEY)")
3699 .execute(conn)
3700 .unwrap();
3701
3702 let update =
3703 "UPDATE sqlite_master SET sql = sql WHERE type = 'table' AND name = 'protected'";
3704
3705 conn.set_writable_schema(false).unwrap();
3707 assert!(crate::sql_query(update).execute(conn).is_err());
3708
3709 conn.set_writable_schema(true).unwrap();
3711 assert!(crate::sql_query(update).execute(conn).is_ok());
3712 }
3713
3714 #[diesel_test_helper::test]
3715 fn fts3_tokenizer_disabled_blocks_the_function() {
3716 let conn = &mut connection();
3717
3718 conn.set_fts3_tokenizer_enabled(true).unwrap();
3720 let enabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
3721 .get_result::<Vec<u8>>(conn);
3722 if enabled.is_err() {
3723 return;
3725 }
3726
3727 conn.set_fts3_tokenizer_enabled(false).unwrap();
3729 let disabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
3730 .get_result::<Vec<u8>>(conn);
3731 assert!(disabled.is_err());
3732 }
3733
3734 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3737 fn temp_db_path(name: &str) -> (tempfile::TempDir, std::path::PathBuf) {
3738 let dir = tempfile::tempdir().unwrap();
3739 let path = dir.path().join(name);
3740 (dir, path)
3741 }
3742
3743 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3744 #[diesel_test_helper::test]
3745 fn attach_create_disabled_blocks_new_database_files() {
3746 let conn = &mut connection();
3747
3748 if conn.set_attach_create_enabled(false).is_err() {
3751 return;
3752 }
3753
3754 let (_dir, path) = temp_db_path("create.db");
3755
3756 assert!(
3758 conn.attach_database(path.to_str().unwrap(), "aux_create")
3759 .is_err()
3760 );
3761
3762 conn.set_attach_create_enabled(true).unwrap();
3764 conn.attach_database(path.to_str().unwrap(), "aux_create")
3765 .unwrap();
3766 conn.detach_database("aux_create").unwrap();
3767 }
3768
3769 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3770 #[diesel_test_helper::test]
3771 fn attach_write_disabled_opens_attached_databases_read_only() {
3772 let conn = &mut connection();
3773
3774 if conn.set_attach_write_enabled(false).is_err() {
3778 return;
3779 }
3780
3781 let (_dir, path) = temp_db_path("write.db");
3783 {
3784 let mut seed = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3785 crate::sql_query("CREATE TABLE t (id INTEGER)")
3786 .execute(&mut seed)
3787 .unwrap();
3788 }
3789
3790 conn.attach_database(path.to_str().unwrap(), "aux_write")
3792 .unwrap();
3793 assert!(
3794 crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
3795 .execute(conn)
3796 .is_err()
3797 );
3798 conn.detach_database("aux_write").unwrap();
3799
3800 conn.set_attach_write_enabled(true).unwrap();
3802 conn.attach_database(path.to_str().unwrap(), "aux_write")
3803 .unwrap();
3804 crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
3805 .execute(conn)
3806 .unwrap();
3807 conn.detach_database("aux_write").unwrap();
3808 }
3809
3810 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3813 table! {
3814 attach_owners (id) {
3815 id -> Integer,
3816 name -> Text,
3817 }
3818 }
3819 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3820 table! {
3821 aux.attach_pets (id) {
3822 id -> Integer,
3823 owner_id -> Integer,
3824 name -> Text,
3825 }
3826 }
3827 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3828 allow_tables_to_appear_in_same_query!(attach_owners, attach_pets);
3829 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3830 table! {
3831 attach_marker (id) {
3832 id -> Integer,
3833 }
3834 }
3835 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3836 table! {
3837 ro.readonly_marker (id) {
3838 id -> Integer,
3839 }
3840 }
3841
3842 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3843 #[diesel_test_helper::test]
3844 fn attach_database_supports_cross_schema_join_then_detach() {
3845 use crate::connection::SimpleConnection;
3846
3847 let conn = &mut connection();
3848
3849 conn.attach_database(":memory:", "aux").unwrap();
3850
3851 conn.batch_execute(
3854 "CREATE TABLE attach_owners (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
3855 CREATE TABLE aux.attach_pets (id INTEGER PRIMARY KEY, owner_id INTEGER, name TEXT NOT NULL);",
3856 )
3857 .unwrap();
3858
3859 crate::insert_into(attach_owners::table)
3860 .values(&[
3861 (attach_owners::id.eq(1), attach_owners::name.eq("Sean")),
3862 (attach_owners::id.eq(2), attach_owners::name.eq("Tess")),
3863 ])
3864 .execute(conn)
3865 .unwrap();
3866 crate::insert_into(attach_pets::table)
3867 .values((
3868 attach_pets::id.eq(1),
3869 attach_pets::owner_id.eq(1),
3870 attach_pets::name.eq("Ferris"),
3871 ))
3872 .execute(conn)
3873 .unwrap();
3874
3875 let pet_owner = attach_owners::table
3876 .inner_join(attach_pets::table.on(attach_pets::owner_id.eq(attach_owners::id)))
3877 .filter(attach_pets::name.eq("Ferris"))
3878 .select(attach_owners::name)
3879 .get_result::<String>(conn)
3880 .unwrap();
3881 assert_eq!(pet_owner, "Sean");
3882
3883 conn.detach_database("aux").unwrap();
3884
3885 assert!(
3887 attach_pets::table
3888 .select(attach_pets::name)
3889 .get_result::<String>(conn)
3890 .is_err()
3891 );
3892 }
3893
3894 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3895 #[diesel_test_helper::test]
3896 fn attach_database_binds_path_verbatim_without_quoting() {
3897 let (_dir, path) = temp_db_path("o'brien.db");
3900
3901 conn_attach_roundtrip(&path);
3902
3903 let mut direct = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3906 let count = attach_marker::table
3907 .count()
3908 .get_result::<i64>(&mut direct)
3909 .unwrap();
3910 assert_eq!(count, 0);
3911 }
3912
3913 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3914 fn conn_attach_roundtrip(path: &std::path::Path) {
3915 use crate::connection::SimpleConnection;
3916
3917 let conn = &mut connection();
3918 conn.attach_database(path.to_str().unwrap(), "verbatim")
3919 .unwrap();
3920 conn.batch_execute("CREATE TABLE verbatim.attach_marker (id INTEGER PRIMARY KEY)")
3921 .unwrap();
3922 conn.detach_database("verbatim").unwrap();
3923 }
3924
3925 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3926 #[diesel_test_helper::test]
3927 fn attach_database_interprets_file_uri_query_parameters() {
3928 let (_dir, path) = temp_db_path("uri_seed.db");
3930 {
3931 let mut seed = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3932 crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
3933 .execute(&mut seed)
3934 .unwrap();
3935 crate::sql_query("INSERT INTO t (id) VALUES (1)")
3936 .execute(&mut seed)
3937 .unwrap();
3938 }
3939
3940 let uri = format!("file:{}?mode=ro", path.display());
3944 let conn = &mut connection();
3945 conn.attach_database(&uri, "ro_schema").unwrap();
3946
3947 let id: i64 = sql::<crate::sql_types::BigInt>("SELECT id FROM ro_schema.t")
3949 .get_result(conn)
3950 .unwrap();
3951 assert_eq!(id, 1);
3952
3953 assert!(
3955 crate::sql_query("INSERT INTO ro_schema.t (id) VALUES (2)")
3956 .execute(conn)
3957 .is_err()
3958 );
3959
3960 conn.detach_database("ro_schema").unwrap();
3961 }
3962
3963 #[diesel_test_helper::test]
3964 fn attach_and_detach_surface_errors_without_panicking() {
3965 let conn = &mut connection();
3966
3967 conn.attach_database(":memory:", "dup").unwrap();
3969 assert!(conn.attach_database(":memory:", "dup").is_err());
3970 conn.detach_database("dup").unwrap();
3971
3972 assert!(conn.detach_database("never_attached").is_err());
3976 }
3977
3978 #[diesel_test_helper::test]
3979 fn attach_database_binds_schema_name_verbatim_without_identifier_quoting() {
3980 use crate::connection::SimpleConnection;
3981
3982 let conn = &mut connection();
3983
3984 let schema = "weird 'schema";
3988 conn.attach_database(":memory:", schema).unwrap();
3989
3990 conn.batch_execute(
3991 r#"CREATE TABLE "weird 'schema".t (id INTEGER PRIMARY KEY);
3992 INSERT INTO "weird 'schema".t (id) VALUES (7);"#,
3993 )
3994 .unwrap();
3995 let id = sql::<Integer>(r#"SELECT id FROM "weird 'schema".t"#)
3996 .get_result::<i32>(conn)
3997 .unwrap();
3998 assert_eq!(id, 7);
3999
4000 conn.detach_database(schema).unwrap();
4001 }
4002
4003 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4004 #[diesel_test_helper::test]
4005 fn attach_database_honors_create_and_write_hardening_knobs() {
4006 use crate::connection::SimpleConnection;
4007
4008 let conn = &mut connection();
4009
4010 if conn.set_attach_create_enabled(false).is_err() {
4012 return;
4013 }
4014
4015 let (_dir_missing, missing) = temp_db_path("nocreate.db");
4017 assert!(
4018 conn.attach_database(missing.to_str().unwrap(), "missing")
4019 .is_err()
4020 );
4021 assert!(!missing.exists());
4022
4023 conn.set_attach_write_enabled(false).unwrap();
4025 let (_dir_existing, existing) = temp_db_path("readonly.db");
4026 {
4027 let mut seed = SqliteConnection::establish(existing.to_str().unwrap()).unwrap();
4028 seed.batch_execute("CREATE TABLE readonly_marker (id INTEGER)")
4029 .unwrap();
4030 }
4031 conn.attach_database(existing.to_str().unwrap(), "ro")
4032 .unwrap();
4033 assert!(
4034 crate::insert_into(readonly_marker::table)
4035 .values(readonly_marker::id.eq(1))
4036 .execute(conn)
4037 .is_err()
4038 );
4039 conn.detach_database("ro").unwrap();
4040 }
4041
4042 #[declare_sql_function]
4045 extern "SQL" {
4046 fn directonly_fn() -> Integer;
4047 fn innocuous_fn() -> Integer;
4048 }
4049
4050 #[diesel_test_helper::test]
4051 fn directonly_function_blocked_from_view() {
4052 let conn = &mut connection();
4053
4054 directonly_fn_utils::register_impl_with_behavior(
4056 conn,
4057 SqliteFunctionBehavior::DIRECTONLY,
4058 || 42,
4059 )
4060 .unwrap();
4061
4062 let result = crate::select(directonly_fn()).get_result::<i32>(conn);
4064 assert_eq!(Ok(42), result);
4065
4066 crate::sql_query("CREATE VIEW test_view AS SELECT directonly_fn() AS val")
4068 .execute(conn)
4069 .unwrap();
4070
4071 conn.set_trusted_schema(false).unwrap();
4073
4074 let result = crate::sql_query("SELECT val FROM test_view").execute(conn);
4076 assert!(result.is_err());
4077 }
4078
4079 #[diesel_test_helper::test]
4080 fn innocuous_function_allowed_from_view_with_untrusted_schema() {
4081 let conn = &mut connection();
4082
4083 innocuous_fn_utils::register_impl_with_behavior(
4085 conn,
4086 SqliteFunctionBehavior::DETERMINISTIC | SqliteFunctionBehavior::INNOCUOUS,
4087 || 99,
4088 )
4089 .unwrap();
4090
4091 crate::sql_query("CREATE VIEW innocuous_view AS SELECT innocuous_fn() AS val")
4093 .execute(conn)
4094 .unwrap();
4095
4096 conn.set_trusted_schema(false).unwrap();
4098
4099 let result = crate::sql_query("SELECT val FROM innocuous_view").execute(conn);
4101 assert!(result.is_ok());
4102 }
4103
4104 #[diesel_test_helper::test]
4105 fn auto_vacuum_all_modes_roundtrip_on_fresh_database() {
4106 for mode in [
4107 AutoVacuumMode::None,
4108 AutoVacuumMode::Full,
4109 AutoVacuumMode::Incremental,
4110 ] {
4111 let conn = &mut connection();
4112 conn.set_auto_vacuum(None, mode).unwrap();
4113 assert_eq!(mode, conn.auto_vacuum(None).unwrap());
4114 }
4115 }
4116
4117 #[diesel_test_helper::test]
4118 fn auto_vacuum_incremental_sticks_across_schema_creation() {
4119 let conn = &mut connection();
4120 conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4121 .unwrap();
4122 assert_eq!(AutoVacuumMode::Incremental, conn.auto_vacuum(None).unwrap());
4123
4124 crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
4125 .execute(conn)
4126 .unwrap();
4127 assert_eq!(
4128 AutoVacuumMode::Incremental,
4129 conn.auto_vacuum(None).unwrap(),
4130 "the mode survives once the schema exists"
4131 );
4132 }
4133
4134 #[diesel_test_helper::test]
4135 fn auto_vacuum_change_from_none_requires_vacuum_on_populated_database() {
4136 let conn = &mut connection();
4137 crate::sql_query("CREATE TABLE t (id INTEGER PRIMARY KEY)")
4138 .execute(conn)
4139 .unwrap();
4140 crate::sql_query("INSERT INTO t (id) VALUES (1)")
4141 .execute(conn)
4142 .unwrap();
4143 assert_eq!(AutoVacuumMode::None, conn.auto_vacuum(None).unwrap());
4144
4145 conn.set_auto_vacuum(None, AutoVacuumMode::Full).unwrap();
4148 assert_eq!(
4149 AutoVacuumMode::None,
4150 conn.auto_vacuum(None).unwrap(),
4151 "the change does not take effect without a VACUUM"
4152 );
4153
4154 crate::sql_query("VACUUM").execute(conn).unwrap();
4155 assert_eq!(
4156 AutoVacuumMode::Full,
4157 conn.auto_vacuum(None).unwrap(),
4158 "VACUUM rewrites the file and applies the mode"
4159 );
4160 }
4161
4162 #[diesel_test_helper::test]
4163 fn auto_vacuum_targets_the_named_attached_database() {
4164 let conn = &mut connection();
4165 crate::sql_query("ATTACH DATABASE ':memory:' AS aux")
4166 .execute(conn)
4167 .unwrap();
4168
4169 conn.set_auto_vacuum(Some("aux"), AutoVacuumMode::Full)
4170 .unwrap();
4171 assert_eq!(AutoVacuumMode::Full, conn.auto_vacuum(Some("aux")).unwrap());
4172 assert_eq!(
4173 AutoVacuumMode::None,
4174 conn.auto_vacuum(None).unwrap(),
4175 "main keeps its own default"
4176 );
4177 }
4178
4179 #[diesel_test_helper::test]
4180 fn auto_vacuum_schema_name_with_double_quote_is_handled() {
4181 let conn = &mut connection();
4182 let schema = r#"we"ird"#;
4183 crate::sql_query(alloc::format!(
4184 r#"ATTACH DATABASE ':memory:' AS "{}""#,
4185 schema.replace('"', "\"\"")
4186 ))
4187 .execute(conn)
4188 .unwrap();
4189
4190 conn.set_auto_vacuum(Some(schema), AutoVacuumMode::Incremental)
4191 .unwrap();
4192 assert_eq!(
4193 AutoVacuumMode::Incremental,
4194 conn.auto_vacuum(Some(schema)).unwrap()
4195 );
4196 }
4197
4198 table! {
4199 pragma_probe (id) {
4200 id -> Integer,
4201 payload -> Text,
4202 }
4203 }
4204
4205 table! {
4206 aux.aux_pragma_probe (id) {
4207 id -> Integer,
4208 payload -> Text,
4209 }
4210 }
4211
4212 const PROBE_TABLE: &str =
4213 "CREATE TABLE pragma_probe (id INTEGER PRIMARY KEY, payload TEXT NOT NULL)";
4214
4215 const AUX_PROBE_TABLE: &str =
4216 "CREATE TABLE aux.aux_pragma_probe (id INTEGER PRIMARY KEY, payload TEXT NOT NULL)";
4217
4218 fn overflowing_payload() -> String {
4221 "x".repeat(64 * 1024)
4222 }
4223
4224 fn insert_overflowing_row(conn: &mut SqliteConnection) {
4225 crate::insert_into(pragma_probe::table)
4226 .values((
4227 pragma_probe::id.eq(1),
4228 pragma_probe::payload.eq(overflowing_payload()),
4229 ))
4230 .execute(conn)
4231 .unwrap();
4232 }
4233
4234 #[diesel_test_helper::test]
4235 fn page_count_is_positive_and_grows() {
4236 let conn = &mut connection();
4237 conn.batch_execute(PROBE_TABLE).unwrap();
4238 let initial = conn.page_count(None).unwrap();
4239 assert!(initial > 0, "an initialized database has at least one page");
4240
4241 insert_overflowing_row(conn);
4242
4243 assert!(
4244 conn.page_count(None).unwrap() > initial,
4245 "a row spanning overflow pages grows the page count"
4246 );
4247 }
4248
4249 #[diesel_test_helper::test]
4250 fn freelist_count_tracks_reclaimable_space() {
4251 let conn = &mut connection();
4252 assert_eq!(
4253 0,
4254 conn.freelist_count(None).unwrap(),
4255 "a fresh database has an empty freelist"
4256 );
4257
4258 conn.batch_execute(PROBE_TABLE).unwrap();
4259 insert_overflowing_row(conn);
4260
4261 crate::delete(pragma_probe::table).execute(conn).unwrap();
4262 assert!(
4263 conn.freelist_count(None).unwrap() > 0,
4264 "deleting the row leaves reclaimable pages on the freelist"
4265 );
4266
4267 crate::sql_query("VACUUM").execute(conn).unwrap();
4269 assert_eq!(
4270 0,
4271 conn.freelist_count(None).unwrap(),
4272 "VACUUM reclaims the freelist"
4273 );
4274 }
4275
4276 #[diesel_test_helper::test]
4277 fn schema_targets_the_named_attached_database() {
4278 let conn = &mut connection();
4279 conn.batch_execute(PROBE_TABLE).unwrap();
4280 conn.attach_database(":memory:", "aux").unwrap();
4281 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4282 crate::insert_into(aux_pragma_probe::table)
4283 .values((
4284 aux_pragma_probe::id.eq(1),
4285 aux_pragma_probe::payload.eq(overflowing_payload()),
4286 ))
4287 .execute(conn)
4288 .unwrap();
4289
4290 let main_pages = conn.page_count(None).unwrap();
4291 let aux_pages = conn.page_count(Some("aux")).unwrap();
4292 assert!(
4293 aux_pages > main_pages,
4294 "the attached database holds the data, main stays small"
4295 );
4296 assert_eq!(
4297 main_pages,
4298 conn.page_count(Some("main")).unwrap(),
4299 "an explicit main matches the default"
4300 );
4301 }
4302
4303 #[diesel_test_helper::test]
4304 fn schema_name_with_backtick_is_escaped() {
4305 let conn = &mut connection();
4308 let schema = "back`tick";
4309 conn.attach_database(":memory:", schema).unwrap();
4310 conn.batch_execute("CREATE TABLE `back``tick`.probe (id INTEGER PRIMARY KEY)")
4311 .unwrap();
4312
4313 assert!(conn.page_count(Some(schema)).unwrap() > 0);
4314 assert_eq!(0, conn.freelist_count(Some(schema)).unwrap());
4315 }
4316
4317 #[diesel_test_helper::test]
4318 fn unknown_schema_is_reported_as_an_error() {
4319 let conn = &mut connection();
4320
4321 assert!(conn.page_count(Some("nope")).is_err());
4322 assert!(conn.freelist_count(Some("nope")).is_err());
4323 }
4324
4325 fn grow_then_empty_freelist(conn: &mut SqliteConnection) {
4328 conn.batch_execute(PROBE_TABLE).unwrap();
4329 let rows = (1..=200)
4330 .map(|id| {
4331 (
4332 pragma_probe::id.eq(id),
4333 pragma_probe::payload.eq("x".repeat(4000)),
4334 )
4335 })
4336 .collect::<Vec<_>>();
4337 crate::insert_into(pragma_probe::table)
4338 .values(rows)
4339 .execute(conn)
4340 .unwrap();
4341 crate::delete(pragma_probe::table).execute(conn).unwrap();
4342 }
4343
4344 fn grow_then_empty_aux_freelist(conn: &mut SqliteConnection) {
4346 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4347 let rows = (1..=200)
4348 .map(|id| {
4349 (
4350 aux_pragma_probe::id.eq(id),
4351 aux_pragma_probe::payload.eq("x".repeat(4000)),
4352 )
4353 })
4354 .collect::<Vec<_>>();
4355 crate::insert_into(aux_pragma_probe::table)
4356 .values(rows)
4357 .execute(conn)
4358 .unwrap();
4359 crate::delete(aux_pragma_probe::table)
4360 .execute(conn)
4361 .unwrap();
4362 }
4363
4364 #[diesel_test_helper::test]
4365 fn incremental_vacuum_clears_the_whole_freelist() {
4366 let conn = &mut connection();
4367 conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4368 .unwrap();
4369 grow_then_empty_freelist(conn);
4370 assert!(
4371 conn.freelist_count(None).unwrap() > 1,
4372 "the deleted rows should leave many pages on the freelist"
4373 );
4374
4375 conn.incremental_vacuum(None, None).unwrap();
4376
4377 assert_eq!(0, conn.freelist_count(None).unwrap());
4380 }
4381
4382 #[diesel_test_helper::test]
4383 fn incremental_vacuum_reclaims_at_most_the_requested_pages() {
4384 let conn = &mut connection();
4385 conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4386 .unwrap();
4387 grow_then_empty_freelist(conn);
4388 let before = conn.freelist_count(None).unwrap();
4389 assert!(before > 10, "the bound has to be smaller than the freelist");
4390
4391 conn.incremental_vacuum(None, Some(10)).unwrap();
4392
4393 let after = conn.freelist_count(None).unwrap();
4394 assert!(after >= before - 10, "at most ten pages may be reclaimed");
4395 assert!(after < before, "some pages should have been reclaimed");
4396 }
4397
4398 #[diesel_test_helper::test]
4399 fn incremental_vacuum_is_a_no_op_outside_incremental_mode() {
4400 let conn = &mut connection();
4401 assert_eq!(AutoVacuumMode::None, conn.auto_vacuum(None).unwrap());
4402 grow_then_empty_freelist(conn);
4403 let before = conn.freelist_count(None).unwrap();
4404 assert!(before > 0);
4405
4406 conn.incremental_vacuum(None, None).unwrap();
4407
4408 assert_eq!(
4409 before,
4410 conn.freelist_count(None).unwrap(),
4411 "a database that is not in incremental mode keeps its freelist"
4412 );
4413 }
4414
4415 #[diesel_test_helper::test]
4416 fn incremental_vacuum_targets_the_named_attached_database() {
4417 let conn = &mut connection();
4418 conn.attach_database(":memory:", "aux").unwrap();
4419 conn.set_auto_vacuum(Some("aux"), AutoVacuumMode::Incremental)
4420 .unwrap();
4421
4422 grow_then_empty_aux_freelist(conn);
4423 assert!(conn.freelist_count(Some("aux")).unwrap() > 0);
4424
4425 conn.incremental_vacuum(Some("aux"), None).unwrap();
4426
4427 assert_eq!(0, conn.freelist_count(Some("aux")).unwrap());
4428 }
4429
4430 #[diesel_test_helper::test]
4431 fn incremental_vacuum_escapes_a_backtick_in_the_schema_name() {
4432 let conn = &mut connection();
4435 let schema = "back`tick";
4436 conn.attach_database(":memory:", schema).unwrap();
4437
4438 conn.incremental_vacuum(Some(schema), None).unwrap();
4439
4440 assert_eq!(0, conn.freelist_count(Some(schema)).unwrap());
4441 }
4442
4443 #[diesel_test_helper::test]
4444 fn incremental_vacuum_of_zero_pages_clears_everything() {
4445 let conn = &mut connection();
4447 conn.set_auto_vacuum(None, AutoVacuumMode::Incremental)
4448 .unwrap();
4449 grow_then_empty_freelist(conn);
4450 assert!(conn.freelist_count(None).unwrap() > 0);
4451
4452 conn.incremental_vacuum(None, Some(0)).unwrap();
4453
4454 assert_eq!(0, conn.freelist_count(None).unwrap());
4455 }
4456
4457 #[diesel_test_helper::test]
4458 fn incremental_vacuum_of_an_unknown_schema_is_an_error() {
4459 let conn = &mut connection();
4460
4461 assert!(conn.incremental_vacuum(Some("nope"), None).is_err());
4462 }
4463
4464 fn fill_then_delete(conn: &mut SqliteConnection) {
4467 conn.batch_execute(PROBE_TABLE).unwrap();
4468 crate::insert_into(pragma_probe::table)
4469 .values((
4470 pragma_probe::id.eq(1),
4471 pragma_probe::payload.eq("x".repeat(256 * 1024)),
4472 ))
4473 .execute(conn)
4474 .unwrap();
4475 crate::delete(pragma_probe::table).execute(conn).unwrap();
4476 crate::insert_into(pragma_probe::table)
4477 .values((pragma_probe::id.eq(2), pragma_probe::payload.eq("kept")))
4478 .execute(conn)
4479 .unwrap();
4480 }
4481
4482 fn fill_then_delete_aux(conn: &mut SqliteConnection) {
4484 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4485 crate::insert_into(aux_pragma_probe::table)
4486 .values((
4487 aux_pragma_probe::id.eq(1),
4488 aux_pragma_probe::payload.eq("x".repeat(256 * 1024)),
4489 ))
4490 .execute(conn)
4491 .unwrap();
4492 crate::delete(aux_pragma_probe::table)
4493 .execute(conn)
4494 .unwrap();
4495 }
4496
4497 #[diesel_test_helper::test]
4498 fn vacuum_repacks_the_database() {
4499 let conn = &mut connection();
4500 fill_then_delete(conn);
4501 let before = conn.page_count(None).unwrap();
4502 assert!(before > 1);
4503
4504 conn.vacuum(None).unwrap();
4505
4506 assert!(
4507 conn.page_count(None).unwrap() < before,
4508 "rebuilding should release the pages the deleted row occupied"
4509 );
4510 assert_eq!(
4511 1,
4512 pragma_probe::table.count().get_result::<i64>(conn).unwrap(),
4513 "the surviving row is still there"
4514 );
4515 }
4516
4517 #[diesel_test_helper::test]
4518 fn vacuum_targets_the_named_attached_database() {
4519 let conn = &mut connection();
4520 conn.attach_database(":memory:", "aux").unwrap();
4521 fill_then_delete_aux(conn);
4522 let before = conn.page_count(Some("aux")).unwrap();
4523 assert!(before > 1);
4524
4525 conn.vacuum(Some("aux")).unwrap();
4526
4527 assert!(conn.page_count(Some("aux")).unwrap() < before);
4528 }
4529
4530 #[diesel_test_helper::test]
4531 fn vacuum_inside_a_transaction_is_an_error() {
4532 use crate::connection::Connection;
4533
4534 let conn = &mut connection();
4535 let result: QueryResult<()> = conn.transaction(|conn| conn.vacuum(None));
4536
4537 assert!(result.is_err());
4538 }
4539
4540 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4541 #[diesel_test_helper::test]
4542 fn vacuum_into_writes_a_readable_copy_through_a_quoted_path() {
4543 let dir = tempfile::tempdir().unwrap();
4544 let destination = dir.path().join("o'brien backup.db");
4547
4548 let conn = &mut connection();
4549 conn.batch_execute(PROBE_TABLE).unwrap();
4550 crate::insert_into(pragma_probe::table)
4551 .values((pragma_probe::id.eq(1), pragma_probe::payload.eq("copied")))
4552 .execute(conn)
4553 .unwrap();
4554
4555 conn.vacuum_into(None, destination.to_str().unwrap())
4556 .unwrap();
4557
4558 let copy = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4559 assert_eq!(
4560 "copied",
4561 pragma_probe::table
4562 .select(pragma_probe::payload)
4563 .get_result::<String>(copy)
4564 .unwrap()
4565 );
4566 }
4567
4568 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4569 #[diesel_test_helper::test]
4570 fn vacuum_into_refuses_to_overwrite_an_existing_database() {
4571 let dir = tempfile::tempdir().unwrap();
4572 let destination = dir.path().join("occupied.db");
4573 {
4574 let occupied = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4575 occupied.batch_execute(PROBE_TABLE).unwrap();
4576 }
4577
4578 let conn = &mut connection();
4579 conn.batch_execute(PROBE_TABLE).unwrap();
4580
4581 assert!(
4582 conn.vacuum_into(None, destination.to_str().unwrap())
4583 .is_err()
4584 );
4585 }
4586
4587 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4588 #[diesel_test_helper::test]
4589 fn vacuum_into_copies_the_named_attached_database() {
4590 let dir = tempfile::tempdir().unwrap();
4591 let destination = dir.path().join("aux copy.db");
4592
4593 let conn = &mut connection();
4594 conn.attach_database(":memory:", "aux").unwrap();
4595 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4596 crate::insert_into(aux_pragma_probe::table)
4597 .values((
4598 aux_pragma_probe::id.eq(7),
4599 aux_pragma_probe::payload.eq("copied"),
4600 ))
4601 .execute(conn)
4602 .unwrap();
4603
4604 conn.vacuum_into(Some("aux"), destination.to_str().unwrap())
4605 .unwrap();
4606
4607 let copy = &mut SqliteConnection::establish(destination.to_str().unwrap()).unwrap();
4608 let id = sql::<Integer>("SELECT id FROM aux_pragma_probe")
4611 .get_result::<i32>(copy)
4612 .unwrap();
4613 assert_eq!(7, id);
4614 }
4615
4616 #[diesel_test_helper::test]
4617 fn vacuum_escapes_a_backtick_in_the_schema_name() {
4618 let conn = &mut connection();
4619 let schema = "back`tick";
4620 conn.attach_database(":memory:", schema).unwrap();
4621
4622 conn.vacuum(Some(schema)).unwrap();
4623 }
4624
4625 #[diesel_test_helper::test]
4626 fn vacuuming_two_schemas_rebuilds_each_of_them() {
4627 let conn = &mut connection();
4631 fill_then_delete(conn);
4632 conn.attach_database(":memory:", "aux").unwrap();
4633 fill_then_delete_aux(conn);
4634
4635 let main_before = conn.page_count(None).unwrap();
4636 let aux_before = conn.page_count(Some("aux")).unwrap();
4637
4638 conn.vacuum(None).unwrap();
4639 conn.vacuum(Some("aux")).unwrap();
4640
4641 assert!(
4642 conn.page_count(None).unwrap() < main_before,
4643 "main was rebuilt"
4644 );
4645 assert!(
4646 conn.page_count(Some("aux")).unwrap() < aux_before,
4647 "aux was rebuilt too, not main a second time"
4648 );
4649 }
4650
4651 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4653 fn wal_connection(path: &std::path::Path) -> SqliteConnection {
4654 let mut conn = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
4655 conn.batch_execute("PRAGMA journal_mode = WAL").unwrap();
4656 conn
4657 }
4658
4659 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4660 #[diesel_test_helper::test]
4661 fn wal_checkpoint_truncate_reports_an_emptied_wal() {
4662 let dir = tempfile::tempdir().unwrap();
4663 let conn = &mut wal_connection(&dir.path().join("wal.db"));
4664 conn.batch_execute(PROBE_TABLE).unwrap();
4665 insert_overflowing_row(conn);
4666
4667 let outcome = conn
4668 .wal_checkpoint(None, WalCheckpointMode::Truncate)
4669 .unwrap();
4670
4671 assert!(!outcome.busy);
4672 assert_eq!(Some(0), outcome.log_frames, "the WAL file was truncated");
4673 assert_eq!(Some(0), outcome.checkpointed_frames);
4674 }
4675
4676 #[diesel_test_helper::test]
4677 fn wal_checkpoint_outside_wal_mode_reports_no_frames() {
4678 let conn = &mut connection();
4679
4680 let outcome = conn
4681 .wal_checkpoint(None, WalCheckpointMode::Truncate)
4682 .unwrap();
4683
4684 assert!(!outcome.busy);
4685 assert_eq!(None, outcome.log_frames);
4686 assert_eq!(None, outcome.checkpointed_frames);
4687 }
4688
4689 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4690 #[diesel_test_helper::test]
4691 fn wal_checkpoint_accepts_every_mode() {
4692 let dir = tempfile::tempdir().unwrap();
4693 let conn = &mut wal_connection(&dir.path().join("modes.db"));
4694 conn.batch_execute(PROBE_TABLE).unwrap();
4695
4696 for (row, mode) in [
4697 WalCheckpointMode::Passive,
4698 WalCheckpointMode::Full,
4699 WalCheckpointMode::Restart,
4700 WalCheckpointMode::Truncate,
4701 WalCheckpointMode::Noop,
4702 ]
4703 .into_iter()
4704 .enumerate()
4705 {
4706 crate::insert_into(pragma_probe::table)
4708 .values((
4709 pragma_probe::id.eq(i32::try_from(row).unwrap() + 1),
4710 pragma_probe::payload.eq("row"),
4711 ))
4712 .execute(conn)
4713 .unwrap();
4714
4715 let outcome = conn.wal_checkpoint(None, mode).unwrap();
4716 assert!(!outcome.busy, "{mode:?} had no competing readers");
4717 assert!(
4718 outcome.log_frames.is_some(),
4719 "{mode:?} ran on a WAL database"
4720 );
4721 assert!(outcome.checkpointed_frames.is_some());
4722 assert!(
4723 outcome.checkpointed_frames <= outcome.log_frames,
4724 "{mode:?}: checkpointed frames cannot exceed the log size"
4725 );
4726 }
4727 }
4728
4729 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4730 #[diesel_test_helper::test]
4731 fn wal_checkpoint_noop_reports_state_without_moving_frames() {
4732 let dir = tempfile::tempdir().unwrap();
4733 let conn = &mut wal_connection(&dir.path().join("noop.db"));
4734
4735 let version = crate::select(sql::<Text>("sqlite_version()"))
4737 .get_result::<String>(conn)
4738 .unwrap();
4739 let mut parts = version.split('.').map(|part| part.parse::<u32>().unwrap());
4740 if (parts.next().unwrap(), parts.next().unwrap()) < (3, 51) {
4741 return;
4742 }
4743
4744 conn.batch_execute(PROBE_TABLE).unwrap();
4745 conn.wal_checkpoint(None, WalCheckpointMode::Truncate)
4746 .unwrap();
4747 crate::insert_into(pragma_probe::table)
4748 .values((pragma_probe::id.eq(1), pragma_probe::payload.eq("noop")))
4749 .execute(conn)
4750 .unwrap();
4751
4752 let first = conn.wal_checkpoint(None, WalCheckpointMode::Noop).unwrap();
4753 let second = conn.wal_checkpoint(None, WalCheckpointMode::Noop).unwrap();
4754
4755 assert!(!first.busy, "NOOP never blocks");
4756 assert!(first.log_frames > Some(0), "the insert sits in the WAL");
4757 assert_eq!(Some(0), first.checkpointed_frames, "nothing was moved");
4758 assert_eq!(first, second, "a second NOOP reports the same state");
4759 }
4760
4761 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4762 #[diesel_test_helper::test]
4763 fn wal_checkpoint_reports_busy_while_a_reader_holds_an_old_snapshot() {
4764 use crate::connection::Connection;
4765
4766 let dir = tempfile::tempdir().unwrap();
4767 let path = dir.path().join("busy.db");
4768 let writer = &mut wal_connection(&path);
4769 writer.batch_execute(PROBE_TABLE).unwrap();
4770 insert_overflowing_row(writer);
4771
4772 let reader = &mut SqliteConnection::establish(path.to_str().unwrap()).unwrap();
4773 reader
4774 .transaction::<_, crate::result::Error, _>(|reader| {
4775 let _ = pragma_probe::table.count().get_result::<i64>(reader)?;
4777
4778 crate::insert_into(pragma_probe::table)
4781 .values((pragma_probe::id.eq(2), pragma_probe::payload.eq("late")))
4782 .execute(writer)?;
4783
4784 let outcome = writer.wal_checkpoint(None, WalCheckpointMode::Passive)?;
4787 assert!(!outcome.busy, "PASSIVE never reports busy");
4788 assert!(
4789 outcome.checkpointed_frames < outcome.log_frames,
4790 "the frames past the reader's snapshot stay in the WAL"
4791 );
4792
4793 for mode in [
4794 WalCheckpointMode::Full,
4795 WalCheckpointMode::Restart,
4796 WalCheckpointMode::Truncate,
4797 ] {
4798 let outcome = writer.wal_checkpoint(None, mode)?;
4799 assert!(outcome.busy, "the open reader blocks a {mode:?} checkpoint");
4800 }
4801 Ok(())
4802 })
4803 .unwrap();
4804
4805 let outcome = writer
4806 .wal_checkpoint(None, WalCheckpointMode::Truncate)
4807 .unwrap();
4808 assert!(
4809 !outcome.busy,
4810 "the checkpoint completes once the reader is done"
4811 );
4812 assert_eq!(Some(0), outcome.log_frames);
4813 }
4814
4815 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4816 #[diesel_test_helper::test]
4817 fn wal_checkpoint_targets_the_named_attached_database() {
4818 let dir = tempfile::tempdir().unwrap();
4819 let conn = &mut connection();
4820 conn.attach_database(dir.path().join("aux.db").to_str().unwrap(), "aux")
4821 .unwrap();
4822 conn.batch_execute("PRAGMA aux.journal_mode = WAL").unwrap();
4823 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4824 crate::insert_into(aux_pragma_probe::table)
4825 .values((
4826 aux_pragma_probe::id.eq(1),
4827 aux_pragma_probe::payload.eq("row"),
4828 ))
4829 .execute(conn)
4830 .unwrap();
4831
4832 let outcome = conn
4833 .wal_checkpoint(Some("aux"), WalCheckpointMode::Truncate)
4834 .unwrap();
4835 assert!(!outcome.busy);
4836 assert_eq!(
4837 Some(0),
4838 outcome.log_frames,
4839 "the attached database was checkpointed"
4840 );
4841
4842 let outcome = conn
4844 .wal_checkpoint(Some("main"), WalCheckpointMode::Truncate)
4845 .unwrap();
4846 assert_eq!(None, outcome.log_frames);
4847 }
4848
4849 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4850 #[diesel_test_helper::test]
4851 fn wal_checkpoint_unqualified_covers_every_attached_database() {
4852 let dir = tempfile::tempdir().unwrap();
4853 let conn = &mut wal_connection(&dir.path().join("main.db"));
4854 conn.batch_execute(PROBE_TABLE).unwrap();
4855 insert_overflowing_row(conn);
4856 conn.attach_database(dir.path().join("aux.db").to_str().unwrap(), "aux")
4857 .unwrap();
4858 conn.batch_execute("PRAGMA aux.journal_mode = WAL").unwrap();
4859 conn.batch_execute(AUX_PROBE_TABLE).unwrap();
4860 crate::insert_into(aux_pragma_probe::table)
4861 .values((
4862 aux_pragma_probe::id.eq(1),
4863 aux_pragma_probe::payload.eq("row"),
4864 ))
4865 .execute(conn)
4866 .unwrap();
4867
4868 conn.wal_checkpoint(None, WalCheckpointMode::Truncate)
4869 .unwrap();
4870
4871 let main_after = conn
4874 .wal_checkpoint(Some("main"), WalCheckpointMode::Passive)
4875 .unwrap();
4876 assert_eq!(Some(0), main_after.log_frames, "main was checkpointed");
4877 let aux_after = conn
4878 .wal_checkpoint(Some("aux"), WalCheckpointMode::Passive)
4879 .unwrap();
4880 assert_eq!(Some(0), aux_after.log_frames, "aux was checkpointed too");
4881 }
4882
4883 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4884 #[diesel_test_helper::test]
4885 fn wal_checkpoint_escapes_a_double_quote_in_the_schema_name() {
4886 let dir = tempfile::tempdir().unwrap();
4889 let conn = &mut connection();
4890 let schema = r#"we"ird"#;
4891 let quoted = schema.replace('"', "\"\"");
4892 conn.attach_database(dir.path().join("weird.db").to_str().unwrap(), schema)
4893 .unwrap();
4894 conn.batch_execute(&alloc::format!(r#"PRAGMA "{quoted}".journal_mode = WAL"#))
4895 .unwrap();
4896 conn.batch_execute(&alloc::format!(
4897 r#"CREATE TABLE "{quoted}".t (id INTEGER PRIMARY KEY)"#
4898 ))
4899 .unwrap();
4900
4901 let outcome = conn
4902 .wal_checkpoint(Some(schema), WalCheckpointMode::Truncate)
4903 .unwrap();
4904 assert_eq!(Some(0), outcome.log_frames, "the quoted schema was reached");
4905 }
4906
4907 #[diesel_test_helper::test]
4908 fn wal_checkpoint_of_an_unknown_schema_is_an_error() {
4909 let conn = &mut connection();
4910
4911 assert!(
4912 conn.wal_checkpoint(Some("nope"), WalCheckpointMode::Passive)
4913 .is_err()
4914 );
4915 }
4916
4917 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
4918 #[diesel_test_helper::test]
4919 fn wal_checkpoint_inside_a_transaction_is_an_error() {
4920 use crate::connection::Connection;
4921
4922 let dir = tempfile::tempdir().unwrap();
4923 let conn = &mut wal_connection(&dir.path().join("txn.db"));
4924 conn.batch_execute(PROBE_TABLE).unwrap();
4925
4926 let result: QueryResult<WalCheckpointOutcome> = conn.transaction(|conn| {
4927 crate::insert_into(pragma_probe::table)
4928 .values((pragma_probe::id.eq(1), pragma_probe::payload.eq("txn")))
4929 .execute(conn)?;
4930 conn.wal_checkpoint(None, WalCheckpointMode::Truncate)
4931 });
4932
4933 assert!(result.is_err(), "SQLite reports SQLITE_LOCKED");
4934 }
4935}