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_source::{ColumnHasTable, NamedTable};
51use crate::result::*;
52use crate::serialize::ToSql;
53use crate::sql_types::{HasSqlType, TypeMetadata};
54use crate::sqlite::{Sqlite, SqliteFunctionBehavior};
55use alloc::string::String;
56use alloc::vec::Vec;
57use core::ffi as libc;
58use core::num::NonZeroI64;
59
60#[allow(missing_debug_implementations)]
183#[cfg(feature = "__sqlite-shared")]
184pub struct SqliteConnection {
185 statement_cache: StatementCache<Sqlite, Statement>,
189 raw_connection: RawConnection,
190 transaction_state: AnsiTransactionManager,
191 metadata_lookup: (),
194 instrumentation: DynInstrumentation,
195 serialized_data: Vec<Vec<u8>>,
206}
207
208#[allow(unsafe_code)]
212unsafe impl Send for SqliteConnection {}
213
214impl SimpleConnection for SqliteConnection {
215 fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
216 self.instrumentation
217 .on_connection_event(InstrumentationEvent::StartQuery {
218 query: &StrQueryHelper::new(query),
219 });
220 let resp = self.raw_connection.exec(query);
221 self.instrumentation
222 .on_connection_event(InstrumentationEvent::FinishQuery {
223 query: &StrQueryHelper::new(query),
224 error: resp.as_ref().err(),
225 });
226 resp
227 }
228}
229
230impl ConnectionSealed for SqliteConnection {}
231
232impl Connection for SqliteConnection {
233 type Backend = Sqlite;
234 type TransactionManager = AnsiTransactionManager;
235
236 fn establish(database_url: &str) -> ConnectionResult<Self> {
252 let mut instrumentation = DynInstrumentation::default_instrumentation();
253 instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
254 url: database_url,
255 });
256
257 let establish_result = Self::establish_inner(database_url);
258 instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
259 url: database_url,
260 error: establish_result.as_ref().err(),
261 });
262 let mut conn = establish_result?;
263 conn.instrumentation = instrumentation;
264 Ok(conn)
265 }
266
267 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
268 where
269 T: QueryFragment<Self::Backend> + QueryId,
270 {
271 let statement_use = self.prepared_query(source)?;
272 statement_use.run().and_then(|_| {
273 self.raw_connection
274 .rows_affected_by_last_query()
275 .map_err(Error::DeserializationError)
276 })
277 }
278
279 fn transaction_state(&mut self) -> &mut AnsiTransactionManager
280 where
281 Self: Sized,
282 {
283 &mut self.transaction_state
284 }
285
286 fn instrumentation(&mut self) -> &mut dyn Instrumentation {
287 &mut *self.instrumentation
288 }
289
290 fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
291 self.instrumentation = instrumentation.into();
292 }
293
294 fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
295 self.statement_cache.set_cache_size(size);
296 }
297}
298
299impl LoadConnection<DefaultLoadingMode> for SqliteConnection {
300 type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>;
301 type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>;
302
303 fn load<'conn, 'query, T>(
304 &'conn mut self,
305 source: T,
306 ) -> QueryResult<Self::Cursor<'conn, 'query>>
307 where
308 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
309 Self::Backend: QueryMetadata<T::SqlType>,
310 {
311 let statement = self.prepared_query(source)?;
312
313 Ok(StatementIterator::new(statement))
314 }
315}
316
317impl WithMetadataLookup for SqliteConnection {
318 fn metadata_lookup(&mut self) -> &mut <Sqlite as TypeMetadata>::MetadataLookup {
319 &mut self.metadata_lookup
320 }
321}
322
323#[cfg(feature = "r2d2")]
324impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection {
325 fn ping(&mut self) -> QueryResult<()> {
326 use crate::RunQueryDsl;
327
328 crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
329 }
330
331 fn is_broken(&mut self) -> bool {
332 AnsiTransactionManager::is_broken_transaction_manager(self)
333 }
334}
335
336impl MultiConnectionHelper for SqliteConnection {
337 fn to_any<'a>(
338 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
339 ) -> &mut (dyn core::any::Any + 'a) {
340 lookup
341 }
342
343 fn from_any(
344 lookup: &mut dyn core::any::Any,
345 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
346 lookup.downcast_mut()
347 }
348}
349
350#[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)]
353pub enum CommitDecision {
354 Proceed,
356 Rollback,
358}
359
360#[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)]
363pub enum ProgressDecision {
364 Continue,
366 Interrupt,
368}
369
370#[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)]
373pub enum BusyDecision {
374 Retry,
376 GiveUp,
378}
379
380impl SqliteConnection {
381 pub fn immediate_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
403 where
404 F: FnOnce(&mut Self) -> Result<T, E>,
405 E: From<Error>,
406 {
407 self.transaction_sql(f, "BEGIN IMMEDIATE")
408 }
409
410 pub fn exclusive_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
432 where
433 F: FnOnce(&mut Self) -> Result<T, E>,
434 E: From<Error>,
435 {
436 self.transaction_sql(f, "BEGIN EXCLUSIVE")
437 }
438
439 pub fn last_insert_rowid(&self) -> Option<NonZeroI64> {
475 NonZeroI64::new(self.raw_connection.last_insert_rowid())
476 }
477
478 pub fn get_read_only_blob<'conn, 'query, U>(
507 &'conn self,
508 blob_column: U,
509 row_id: i64,
510 ) -> Result<sqlite_blob::SqliteReadOnlyBlob<'conn>, Error>
511 where
512 'query: 'conn,
513 U: ColumnHasTable,
514 U::Table: NamedTable,
515 {
516 let table = blob_column.table();
517
518 let database_name = table.schema().unwrap_or("main");
519 let column_name = blob_column.name();
520 let table_name = table.table();
521
522 self.raw_connection
523 .blob_open(database_name, table_name, column_name, row_id)
524 }
525
526 fn transaction_sql<T, E, F>(&mut self, f: F, sql: &str) -> Result<T, E>
527 where
528 F: FnOnce(&mut Self) -> Result<T, E>,
529 E: From<Error>,
530 {
531 AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?;
532 match f(&mut *self) {
533 Ok(value) => {
534 AnsiTransactionManager::commit_transaction(&mut *self)?;
535 Ok(value)
536 }
537 Err(e) => {
538 AnsiTransactionManager::rollback_transaction(&mut *self)?;
539 Err(e)
540 }
541 }
542 }
543
544 fn prepared_query<'conn, 'query, T>(
545 &'conn mut self,
546 source: T,
547 ) -> QueryResult<StatementUse<'conn, 'query>>
548 where
549 T: QueryFragment<Sqlite> + QueryId + 'query,
550 {
551 self.instrumentation
552 .on_connection_event(InstrumentationEvent::StartQuery {
553 query: &crate::debug_query(&source),
554 });
555 let raw_connection = &self.raw_connection;
556 let cache = &mut self.statement_cache;
557 let statement = match cache.cached_statement(
558 &source,
559 &Sqlite,
560 &[],
561 raw_connection,
562 Statement::prepare,
563 &mut *self.instrumentation,
564 ) {
565 Ok(statement) => statement,
566 Err(e) => {
567 self.instrumentation
568 .on_connection_event(InstrumentationEvent::FinishQuery {
569 query: &crate::debug_query(&source),
570 error: Some(&e),
571 });
572
573 return Err(e);
574 }
575 };
576
577 StatementUse::bind(statement, source, &mut *self.instrumentation)
578 }
579
580 #[doc(hidden)]
581 pub fn register_sql_function<ArgsSqlType, RetSqlType, Args, Ret, F>(
582 &mut self,
583 fn_name: &str,
584 behavior: SqliteFunctionBehavior,
585 mut f: F,
586 ) -> QueryResult<()>
587 where
588 F: FnMut(Args) -> Ret + core::panic::UnwindSafe + Send + 'static,
589 Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
590 Ret: ToSql<RetSqlType, Sqlite>,
591 Sqlite: HasSqlType<RetSqlType>,
592 {
593 functions::register(&self.raw_connection, fn_name, behavior, move |_, args| {
594 f(args)
595 })
596 }
597
598 #[doc(hidden)]
599 pub fn register_noarg_sql_function<RetSqlType, Ret, F>(
600 &mut self,
601 fn_name: &str,
602 behavior: SqliteFunctionBehavior,
603 f: F,
604 ) -> QueryResult<()>
605 where
606 F: FnMut() -> Ret + core::panic::UnwindSafe + Send + 'static,
607 Ret: ToSql<RetSqlType, Sqlite>,
608 Sqlite: HasSqlType<RetSqlType>,
609 {
610 functions::register_noargs(&self.raw_connection, fn_name, behavior, f)
611 }
612
613 #[doc(hidden)]
614 pub fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
615 &mut self,
616 fn_name: &str,
617 behavior: SqliteFunctionBehavior,
618 ) -> QueryResult<()>
619 where
620 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + core::panic::UnwindSafe,
621 Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
622 Ret: ToSql<RetSqlType, Sqlite>,
623 Sqlite: HasSqlType<RetSqlType>,
624 {
625 functions::register_aggregate::<_, _, _, _, A>(&self.raw_connection, fn_name, behavior)
626 }
627
628 pub fn register_collation<F>(&mut self, collation_name: &str, collation: F) -> QueryResult<()>
664 where
665 F: Fn(&str, &str) -> core::cmp::Ordering + Send + 'static + core::panic::UnwindSafe,
666 {
667 self.raw_connection
668 .register_collation_function(collation_name, collation)
669 }
670
671 pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase {
680 self.raw_connection.serialize()
681 }
682
683 #[allow(unsafe_code)]
723 pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> {
724 self.serialized_data.push(data.to_vec());
727 let last = self
728 .serialized_data
729 .last()
730 .expect("We literally pushed it above, so it's there");
731 unsafe {
732 self.raw_connection.deserialize(last)
735 }
736 }
737
738 #[allow(unsafe_code)]
825 pub unsafe fn with_raw_connection<R, F>(&mut self, f: F) -> R
826 where
827 F: FnOnce(*mut ffi::sqlite3) -> R,
828 {
829 f(self.raw_connection.internal_connection.as_ptr())
830 }
831
832 #[allow(unsafe_code)]
841 pub(crate) unsafe fn with_borrowed_connection<R>(
842 db: core::ptr::NonNull<ffi::sqlite3>,
843 f: impl FnOnce(&mut SqliteConnection) -> R,
844 ) -> R {
845 struct Borrowed(core::mem::ManuallyDrop<SqliteConnection>);
848
849 impl Drop for Borrowed {
850 fn drop(&mut self) {
851 let conn = unsafe { core::mem::ManuallyDrop::take(&mut self.0) };
853 let SqliteConnection {
854 statement_cache,
855 raw_connection,
856 ..
857 } = conn;
858 drop(statement_cache);
861 core::mem::forget(raw_connection);
862 }
863 }
864
865 let mut conn = Borrowed(core::mem::ManuallyDrop::new(SqliteConnection {
866 statement_cache: StatementCache::new(),
867 raw_connection: RawConnection::from_ptr(db),
868 transaction_state: AnsiTransactionManager::default(),
869 metadata_lookup: (),
870 instrumentation: DynInstrumentation::default_instrumentation(),
871 serialized_data: Vec::new(),
872 }));
873
874 let result = f(&mut conn.0);
875
876 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!(
879 matches!(
880 AnsiTransactionManager::transaction_manager_status_mut(&mut *conn.0)
881 .transaction_depth(),
882 Ok(None)
883 ),
884 "callback must not leave an open transaction on the borrowed connection"
885 );
886
887 result
888 }
889
890 pub fn set_limit(&mut self, limit: SqliteLimit, value: i32) -> i32 {
913 self.raw_connection.set_limit(limit, value)
914 }
915
916 pub fn get_limit(&self, limit: SqliteLimit) -> i32 {
934 self.raw_connection.get_limit(limit)
935 }
936
937 pub fn set_recommended_security_limits(&mut self) {
980 self.set_limit(SqliteLimit::Length, SqliteLimit::SAFE_LENGTH_LIMIT);
981 self.set_limit(SqliteLimit::SqlLength, SqliteLimit::SAFE_SQL_LENGTH_LIMIT);
982 self.set_limit(
983 SqliteLimit::ColumnCount,
984 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT,
985 );
986 self.set_limit(SqliteLimit::ExprDepth, SqliteLimit::SAFE_EXPR_DEPTH_LIMIT);
987 self.set_limit(
988 SqliteLimit::CompoundSelect,
989 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT,
990 );
991 self.set_limit(SqliteLimit::VdbeOp, SqliteLimit::SAFE_VDBE_OP_LIMIT);
992 self.set_limit(
993 SqliteLimit::FunctionArg,
994 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT,
995 );
996 self.set_limit(SqliteLimit::Attached, SqliteLimit::SAFE_ATTACHED_LIMIT);
997 self.set_limit(
998 SqliteLimit::LikePatternLength,
999 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT,
1000 );
1001 self.set_limit(
1002 SqliteLimit::VariableNumber,
1003 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT,
1004 );
1005 self.set_limit(
1006 SqliteLimit::TriggerDepth,
1007 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT,
1008 );
1009 }
1010
1011 pub fn set_defensive(&mut self, enabled: bool) -> QueryResult<()> {
1038 self.raw_connection
1039 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE, enabled)
1040 }
1041
1042 pub fn is_defensive(&self) -> QueryResult<bool> {
1046 self.raw_connection
1047 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE)
1048 }
1049
1050 pub fn set_trusted_schema(&mut self, trusted: bool) -> QueryResult<()> {
1062 self.raw_connection
1063 .set_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA, trusted)
1064 }
1065
1066 pub fn is_trusted_schema(&self) -> QueryResult<bool> {
1070 self.raw_connection
1071 .get_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA)
1072 }
1073
1074 pub fn with_load_extension_enabled<R, E>(
1102 &mut self,
1103 f: impl FnOnce(&mut Self) -> Result<R, E>,
1104 ) -> Result<R, E>
1105 where
1106 E: From<crate::result::Error>,
1107 {
1108 self.set_load_extension_enabled(true)?;
1109
1110 #[cfg(feature = "std")]
1114 {
1115 match std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| f(self))) {
1116 Ok(r) => {
1117 self.set_load_extension_enabled(false)?;
1118 r
1119 }
1120 Err(panic) => {
1121 let _ = self.set_load_extension_enabled(false);
1122 std::panic::resume_unwind(panic);
1123 }
1124 }
1125 }
1126 #[cfg(not(feature = "std"))]
1127 {
1128 let r = f(self);
1129 self.set_load_extension_enabled(false)?;
1130 r
1131 }
1132 }
1133
1134 fn set_load_extension_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1135 self.raw_connection
1136 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, enabled)
1137 }
1138
1139 #[cfg(test)]
1140 fn is_load_extension_enabled(&self) -> QueryResult<bool> {
1141 self.raw_connection
1142 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION)
1143 }
1144
1145 pub fn set_fts3_tokenizer_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1154 self.raw_connection
1155 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, enabled)
1156 }
1157
1158 pub fn is_fts3_tokenizer_enabled(&self) -> QueryResult<bool> {
1162 self.raw_connection
1163 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER)
1164 }
1165
1166 pub fn set_writable_schema(&mut self, enabled: bool) -> QueryResult<()> {
1175 self.raw_connection
1176 .set_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA, enabled)
1177 }
1178
1179 pub fn is_writable_schema(&self) -> QueryResult<bool> {
1183 self.raw_connection
1184 .get_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA)
1185 }
1186
1187 pub fn set_attach_create_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1195 self.raw_connection
1196 .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, enabled)
1197 }
1198
1199 pub fn is_attach_create_enabled(&self) -> QueryResult<bool> {
1203 self.raw_connection
1204 .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE)
1205 }
1206
1207 pub fn set_attach_write_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1214 self.raw_connection
1215 .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, enabled)
1216 }
1217
1218 pub fn is_attach_write_enabled(&self) -> QueryResult<bool> {
1222 self.raw_connection
1223 .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE)
1224 }
1225
1226 pub fn set_triggers_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1232 self.raw_connection
1233 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER, enabled)
1234 }
1235
1236 pub fn are_triggers_enabled(&self) -> QueryResult<bool> {
1240 self.raw_connection
1241 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER)
1242 }
1243
1244 pub fn set_views_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1250 self.raw_connection
1251 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW, enabled)
1252 }
1253
1254 pub fn are_views_enabled(&self) -> QueryResult<bool> {
1258 self.raw_connection
1259 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW)
1260 }
1261
1262 pub fn set_foreign_keys_enabled(&mut self, enabled: bool) -> QueryResult<()> {
1268 self.raw_connection
1269 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY, enabled)
1270 }
1271
1272 pub fn are_foreign_keys_enabled(&self) -> QueryResult<bool> {
1276 self.raw_connection
1277 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY)
1278 }
1279
1280 pub fn set_double_quoted_strings_dml(&mut self, enabled: bool) -> QueryResult<()> {
1288 self.raw_connection
1289 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML, enabled)
1290 }
1291
1292 pub fn are_double_quoted_strings_dml_enabled(&self) -> QueryResult<bool> {
1296 self.raw_connection
1297 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML)
1298 }
1299
1300 pub fn set_double_quoted_strings_ddl(&mut self, enabled: bool) -> QueryResult<()> {
1308 self.raw_connection
1309 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL, enabled)
1310 }
1311
1312 pub fn are_double_quoted_strings_ddl_enabled(&self) -> QueryResult<bool> {
1316 self.raw_connection
1317 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL)
1318 }
1319
1320 fn register_diesel_sql_functions(&self) -> QueryResult<()> {
1321 use crate::sql_types::{Integer, Text};
1322
1323 functions::register::<Text, Integer, _, _, _>(
1327 &self.raw_connection,
1328 "diesel_manage_updated_at",
1329 SqliteFunctionBehavior::DIRECTONLY,
1330 |conn, table_name: String| {
1331 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!(
1332 include_str!("diesel_manage_updated_at.sql"),
1333 table_name = table_name
1334 ))
1335 .expect("Failed to create trigger");
1336 0 },
1338 )
1339 }
1340
1341 fn establish_inner(database_url: &str) -> Result<SqliteConnection, ConnectionError> {
1342 use crate::result::ConnectionError::CouldntSetupConfiguration;
1343 let raw_connection = RawConnection::establish(database_url)?;
1344 let conn = Self {
1345 statement_cache: StatementCache::new(),
1346 raw_connection,
1347 transaction_state: AnsiTransactionManager::default(),
1348 metadata_lookup: (),
1349 instrumentation: DynInstrumentation::none(),
1350 serialized_data: Vec::new(),
1351 };
1352 conn.register_diesel_sql_functions()
1353 .map_err(CouldntSetupConfiguration)?;
1354 Ok(conn)
1355 }
1356}
1357
1358fn error_message(err_code: libc::c_int) -> &'static str {
1359 ffi::code_to_str(err_code)
1360}
1361
1362#[cfg(test)]
1363mod tests {
1364 use super::*;
1365 use crate::dsl::sql;
1366 use crate::prelude::*;
1367 use crate::sql_types::{Integer, Text};
1368 use crate::sqlite::SqliteFunctionBehavior;
1369
1370 fn connection() -> SqliteConnection {
1371 SqliteConnection::establish(":memory:").unwrap()
1372 }
1373
1374 #[diesel_test_helper::test]
1375 #[allow(unsafe_code)]
1376 fn with_raw_connection_can_return_values() {
1377 let connection = &mut connection();
1378
1379 let autocommit_status = unsafe {
1381 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1382 };
1383
1384 assert_ne!(autocommit_status, 0, "Expected autocommit to be enabled");
1386 }
1387
1388 #[diesel_test_helper::test]
1389 #[allow(unsafe_code)]
1390 fn with_raw_connection_works_after_diesel_operations() {
1391 let connection = &mut connection();
1392
1393 crate::sql_query("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")
1395 .execute(connection)
1396 .unwrap();
1397 crate::sql_query("INSERT INTO test_table (value) VALUES ('hello')")
1398 .execute(connection)
1399 .unwrap();
1400
1401 let last_rowid = unsafe {
1403 connection.with_raw_connection(|raw_conn| ffi::sqlite3_last_insert_rowid(raw_conn))
1404 };
1405
1406 assert_eq!(last_rowid, 1, "Last insert rowid should be 1");
1407
1408 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM test_table")
1410 .get_result(connection)
1411 .unwrap();
1412 assert_eq!(count, 1);
1413 }
1414
1415 #[diesel_test_helper::test]
1416 #[allow(unsafe_code)]
1417 fn with_raw_connection_can_execute_raw_sql() {
1418 let connection = &mut connection();
1419
1420 crate::sql_query("CREATE TABLE raw_test (id INTEGER PRIMARY KEY, name TEXT)")
1422 .execute(connection)
1423 .unwrap();
1424
1425 let result = unsafe {
1428 connection.with_raw_connection(|raw_conn| {
1429 let sql = c"INSERT INTO raw_test (name) VALUES ('from_raw')";
1430 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1431 let rc = ffi::sqlite3_exec(
1432 raw_conn,
1433 sql.as_ptr(),
1434 None,
1435 core::ptr::null_mut(),
1436 &mut err_msg,
1437 );
1438 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1439 ffi::sqlite3_free(err_msg as *mut libc::c_void);
1440 }
1441 rc
1442 })
1443 };
1444
1445 assert_eq!(result, ffi::SQLITE_OK, "Raw SQL execution should succeed");
1446
1447 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM raw_test")
1449 .get_result(connection)
1450 .unwrap();
1451 assert_eq!(count, 1);
1452
1453 let name: String = sql::<Text>("SELECT name FROM raw_test WHERE id = 1")
1454 .get_result(connection)
1455 .unwrap();
1456 assert_eq!(name, "from_raw");
1457 }
1458
1459 #[diesel_test_helper::test]
1460 #[allow(unsafe_code)]
1461 fn with_raw_connection_works_within_transaction() {
1462 let connection = &mut connection();
1463
1464 crate::sql_query("CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value INTEGER)")
1465 .execute(connection)
1466 .unwrap();
1467
1468 connection
1469 .transaction::<_, crate::result::Error, _>(|conn| {
1470 crate::sql_query("INSERT INTO txn_test (value) VALUES (42)")
1471 .execute(conn)
1472 .unwrap();
1473
1474 let autocommit = unsafe {
1476 conn.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1477 };
1478
1479 assert_eq!(
1481 autocommit, 0,
1482 "Autocommit should be disabled inside transaction"
1483 );
1484
1485 Ok(())
1486 })
1487 .unwrap();
1488
1489 let autocommit = unsafe {
1491 connection.with_raw_connection(|raw_conn| ffi::sqlite3_get_autocommit(raw_conn))
1492 };
1493 assert_ne!(
1494 autocommit, 0,
1495 "Autocommit should be enabled after transaction"
1496 );
1497 }
1498
1499 #[diesel_test_helper::test]
1500 #[allow(unsafe_code)]
1501 fn with_raw_connection_can_read_database_filename() {
1502 let connection = &mut connection();
1503
1504 let filename = unsafe {
1506 connection.with_raw_connection(|raw_conn| {
1507 let db_name = c"main";
1508 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1509 if filename_ptr.is_null() {
1510 None
1511 } else {
1512 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1514 Some(cstr.to_string_lossy().into_owned())
1515 }
1516 })
1517 };
1518
1519 assert_eq!(
1522 filename,
1523 Some(String::new()),
1524 "In-memory database filename should be an empty string"
1525 );
1526 }
1527
1528 #[diesel_test_helper::test]
1529 #[allow(unsafe_code)]
1530 fn with_raw_connection_changes_count() {
1531 let connection = &mut connection();
1532
1533 crate::sql_query("CREATE TABLE changes_test (id INTEGER PRIMARY KEY, value INTEGER)")
1534 .execute(connection)
1535 .unwrap();
1536
1537 crate::sql_query("INSERT INTO changes_test (value) VALUES (1), (2), (3)")
1538 .execute(connection)
1539 .unwrap();
1540
1541 let changes = unsafe {
1543 connection.with_raw_connection(|raw_conn| {
1544 let sql = c"UPDATE changes_test SET value = value + 10";
1545 let mut err_msg: *mut libc::c_char = core::ptr::null_mut();
1546 let rc = ffi::sqlite3_exec(
1547 raw_conn,
1548 sql.as_ptr(),
1549 None,
1550 core::ptr::null_mut(),
1551 &mut err_msg,
1552 );
1553 if rc != ffi::SQLITE_OK && !err_msg.is_null() {
1554 ffi::sqlite3_free(err_msg as *mut libc::c_void);
1555 return -1;
1556 }
1557 ffi::sqlite3_changes(raw_conn)
1558 })
1559 };
1560
1561 assert_eq!(changes, 3, "Should have updated 3 rows");
1562
1563 let values: Vec<i32> = sql::<Integer>("SELECT value FROM changes_test ORDER BY id")
1565 .load(connection)
1566 .unwrap();
1567 assert_eq!(values, vec![11, 12, 13]);
1568 }
1569
1570 #[diesel_test_helper::test]
1572 #[allow(unsafe_code)]
1573 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1574 fn with_raw_connection_recovers_after_panic() {
1575 let connection = &mut connection();
1576
1577 crate::sql_query("CREATE TABLE panic_test (id INTEGER PRIMARY KEY, value TEXT)")
1578 .execute(connection)
1579 .unwrap();
1580
1581 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
1583 connection.with_raw_connection(|_raw_conn| {
1584 panic!("intentional panic inside with_raw_connection");
1585 })
1586 }));
1587 assert!(result.is_err(), "Should have caught the panic");
1588
1589 crate::sql_query("INSERT INTO panic_test (value) VALUES ('after_panic')")
1591 .execute(connection)
1592 .unwrap();
1593
1594 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM panic_test")
1595 .get_result(connection)
1596 .unwrap();
1597 assert_eq!(count, 1, "Connection should work after panic in callback");
1598 }
1599
1600 #[diesel_test_helper::test]
1602 #[allow(unsafe_code)]
1603 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
1604 fn with_raw_connection_can_read_file_database_filename() {
1605 let dir = std::env::temp_dir().join("diesel_test_filename.db");
1606 let db_path = dir.to_str().unwrap();
1607
1608 let _ = std::fs::remove_file(db_path);
1610
1611 let connection = &mut SqliteConnection::establish(db_path).unwrap();
1612
1613 let filename = unsafe {
1615 connection.with_raw_connection(|raw_conn| {
1616 let db_name = c"main";
1617 let filename_ptr = ffi::sqlite3_db_filename(raw_conn, db_name.as_ptr());
1618 if filename_ptr.is_null() {
1619 None
1620 } else {
1621 let cstr = core::ffi::CStr::from_ptr(filename_ptr);
1622 Some(cstr.to_string_lossy().into_owned())
1623 }
1624 })
1625 };
1626
1627 let filename = filename.expect("File-based database should have a filename");
1628 assert!(
1629 filename.contains("diesel_test_filename.db"),
1630 "Filename should contain the database name, got: {filename}"
1631 );
1632
1633 let _ = std::fs::remove_file(db_path);
1635 }
1636
1637 #[declare_sql_function]
1638 extern "SQL" {
1639 fn fun_case(x: Text) -> Text;
1640 fn my_add(x: Integer, y: Integer) -> Integer;
1641 fn answer() -> Integer;
1642 fn add_counter(x: Integer) -> Integer;
1643
1644 #[aggregate]
1645 fn my_sum(expr: Integer) -> Integer;
1646 #[aggregate]
1647 fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable<Integer>;
1648 }
1649
1650 #[diesel_test_helper::test]
1651 fn database_serializes_and_deserializes_successfully() {
1652 let expected_users = vec![
1653 (
1654 1,
1655 "John Doe".to_string(),
1656 "john.doe@example.com".to_string(),
1657 ),
1658 (
1659 2,
1660 "Jane Doe".to_string(),
1661 "jane.doe@example.com".to_string(),
1662 ),
1663 ];
1664
1665 let conn1 = &mut connection();
1666 let _ =
1667 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
1668 .execute(conn1);
1669 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
1670 .execute(conn1);
1671
1672 for _i in 0..2 {
1673 let serialized_database = conn1.serialize_database_to_buffer();
1674 let conn2 = &mut connection();
1675 conn2
1676 .deserialize_readonly_database_from_buffer(serialized_database.as_slice())
1677 .unwrap();
1678
1679 let query =
1680 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
1681 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
1682
1683 assert_eq!(expected_users, actual_users);
1684 std::mem::drop(serialized_database);
1688 let query =
1689 sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
1690 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
1691
1692 assert_eq!(expected_users, actual_users);
1693 }
1694 }
1695
1696 #[diesel_test_helper::test]
1697 fn database_deserialize_random_bytes() {
1698 let buffer = vec![0, 1, 2, 3, 4];
1699 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1700
1701 conn.deserialize_readonly_database_from_buffer(&buffer)
1702 .unwrap();
1703
1704 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
1705
1706 assert!(r.is_err());
1707 assert_eq!(r.unwrap_err().to_string(), "file is not a database");
1708
1709 let conn = &mut SqliteConnection::establish(":memory:").unwrap();
1710
1711 let _ =
1712 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
1713 .execute(conn);
1714 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
1715 .execute(conn);
1716
1717 let db = conn.serialize_database_to_buffer();
1718 let mut bad_buffer = db[..100].to_vec();
1720 bad_buffer.extend(b"whatever");
1721 conn.deserialize_readonly_database_from_buffer(&bad_buffer)
1722 .unwrap();
1723
1724 let r = sql::<Integer>("SELECT id FROM users").load::<i32>(conn);
1725
1726 assert!(r.is_err());
1727 assert_eq!(
1728 r.unwrap_err().to_string(),
1729 "database disk image is malformed"
1730 );
1731
1732 let mut size_fitting_bad_buffer = db[..100].to_vec();
1734 size_fitting_bad_buffer.extend(
1735 core::iter::repeat(b"abcdefghij")
1736 .flatten()
1737 .take(db.len() - 100),
1738 );
1739 let r = conn.deserialize_readonly_database_from_buffer(&size_fitting_bad_buffer);
1740
1741 assert!(r.is_err());
1742 assert_eq!(
1743 r.unwrap_err().to_string(),
1744 "database disk image is malformed"
1745 );
1746 }
1747
1748 #[diesel_test_helper::test]
1749 fn register_custom_function() {
1750 let connection = &mut connection();
1751 fun_case_utils::register_impl(connection, |x: String| {
1752 x.chars()
1753 .enumerate()
1754 .map(|(i, c)| {
1755 if i % 2 == 0 {
1756 c.to_lowercase().to_string()
1757 } else {
1758 c.to_uppercase().to_string()
1759 }
1760 })
1761 .collect::<String>()
1762 })
1763 .unwrap();
1764
1765 let mapped_string = crate::select(fun_case("foobar"))
1766 .get_result::<String>(connection)
1767 .unwrap();
1768 assert_eq!("fOoBaR", mapped_string);
1769 }
1770
1771 #[diesel_test_helper::test]
1772 fn register_multiarg_function() {
1773 let connection = &mut connection();
1774 my_add_utils::register_impl(connection, |x: i32, y: i32| x + y).unwrap();
1775
1776 let added = crate::select(my_add(1, 2)).get_result::<i32>(connection);
1777 assert_eq!(Ok(3), added);
1778 }
1779
1780 #[diesel_test_helper::test]
1781 fn register_noarg_function() {
1782 let connection = &mut connection();
1783 answer_utils::register_impl(connection, || 42).unwrap();
1784
1785 let answer = crate::select(answer()).get_result::<i32>(connection);
1786 assert_eq!(Ok(42), answer);
1787 }
1788
1789 #[diesel_test_helper::test]
1790 fn register_nondeterministic_noarg_function() {
1791 let connection = &mut connection();
1792 answer_utils::register_nondeterministic_impl(connection, || 42).unwrap();
1793
1794 let answer = crate::select(answer()).get_result::<i32>(connection);
1795 assert_eq!(Ok(42), answer);
1796 }
1797
1798 #[diesel_test_helper::test]
1799 fn register_nondeterministic_function() {
1800 let connection = &mut connection();
1801 let mut y = 0;
1802 add_counter_utils::register_nondeterministic_impl(connection, move |x: i32| {
1803 y += 1;
1804 x + y
1805 })
1806 .unwrap();
1807
1808 let added = crate::select((add_counter(1), add_counter(1), add_counter(1)))
1809 .get_result::<(i32, i32, i32)>(connection);
1810 assert_eq!(Ok((2, 3, 4)), added);
1811 }
1812
1813 #[derive(Default)]
1814 struct MySum {
1815 sum: i32,
1816 }
1817
1818 impl SqliteAggregateFunction<i32> for MySum {
1819 type Output = i32;
1820
1821 fn step(&mut self, expr: i32) {
1822 self.sum += expr;
1823 }
1824
1825 fn finalize(aggregator: Option<Self>) -> Self::Output {
1826 aggregator.map(|a| a.sum).unwrap_or_default()
1827 }
1828 }
1829
1830 table! {
1831 my_sum_example {
1832 id -> Integer,
1833 value -> Integer,
1834 }
1835 }
1836
1837 #[diesel_test_helper::test]
1838 fn register_aggregate_function() {
1839 use self::my_sum_example::dsl::*;
1840
1841 let connection = &mut connection();
1842 crate::sql_query(
1843 "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
1844 )
1845 .execute(connection)
1846 .unwrap();
1847 crate::sql_query("INSERT INTO my_sum_example (value) VALUES (1), (2), (3)")
1848 .execute(connection)
1849 .unwrap();
1850
1851 my_sum_utils::register_impl_with_behavior::<MySum, _>(
1852 connection,
1853 SqliteFunctionBehavior::DETERMINISTIC,
1854 )
1855 .unwrap();
1856
1857 let result = my_sum_example
1858 .select(my_sum(value))
1859 .get_result::<i32>(connection);
1860 assert_eq!(Ok(6), result);
1861 }
1862
1863 #[diesel_test_helper::test]
1864 fn register_aggregate_function_returns_finalize_default_on_empty_set() {
1865 use self::my_sum_example::dsl::*;
1866
1867 let connection = &mut connection();
1868 crate::sql_query(
1869 "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
1870 )
1871 .execute(connection)
1872 .unwrap();
1873
1874 my_sum_utils::register_impl_with_behavior::<MySum, _>(
1875 connection,
1876 SqliteFunctionBehavior::DETERMINISTIC,
1877 )
1878 .unwrap();
1879
1880 let result = my_sum_example
1881 .select(my_sum(value))
1882 .get_result::<i32>(connection);
1883 assert_eq!(Ok(0), result);
1884 }
1885
1886 #[derive(Default)]
1887 struct RangeMax<T> {
1888 max_value: Option<T>,
1889 }
1890
1891 impl<T: Default + Ord + Copy + Clone> SqliteAggregateFunction<(T, T, T)> for RangeMax<T> {
1892 type Output = Option<T>;
1893
1894 fn step(&mut self, (x0, x1, x2): (T, T, T)) {
1895 let max = if x0 >= x1 && x0 >= x2 {
1896 x0
1897 } else if x1 >= x0 && x1 >= x2 {
1898 x1
1899 } else {
1900 x2
1901 };
1902
1903 self.max_value = match self.max_value {
1904 Some(current_max_value) if max > current_max_value => Some(max),
1905 None => Some(max),
1906 _ => self.max_value,
1907 };
1908 }
1909
1910 fn finalize(aggregator: Option<Self>) -> Self::Output {
1911 aggregator?.max_value
1912 }
1913 }
1914
1915 table! {
1916 range_max_example {
1917 id -> Integer,
1918 value1 -> Integer,
1919 value2 -> Integer,
1920 value3 -> Integer,
1921 }
1922 }
1923
1924 #[diesel_test_helper::test]
1925 fn register_aggregate_multiarg_function() {
1926 use self::range_max_example::dsl::*;
1927
1928 let connection = &mut connection();
1929 crate::sql_query(
1930 r#"CREATE TABLE range_max_example (
1931 id integer primary key autoincrement,
1932 value1 integer,
1933 value2 integer,
1934 value3 integer
1935 )"#,
1936 )
1937 .execute(connection)
1938 .unwrap();
1939 crate::sql_query(
1940 "INSERT INTO range_max_example (value1, value2, value3) VALUES (3, 2, 1), (2, 2, 2)",
1941 )
1942 .execute(connection)
1943 .unwrap();
1944
1945 range_max_utils::register_impl_with_behavior::<RangeMax<i32>, _, _, _>(
1946 connection,
1947 SqliteFunctionBehavior::DETERMINISTIC,
1948 )
1949 .unwrap();
1950 let result = range_max_example
1951 .select(range_max(value1, value2, value3))
1952 .get_result::<Option<i32>>(connection)
1953 .unwrap();
1954 assert_eq!(Some(3), result);
1955 }
1956
1957 table! {
1958 my_collation_example {
1959 id -> Integer,
1960 value -> Text,
1961 }
1962 }
1963
1964 #[diesel_test_helper::test]
1965 fn register_collation_function() {
1966 use self::my_collation_example::dsl::*;
1967
1968 let connection = &mut connection();
1969
1970 connection
1971 .register_collation("RUSTNOCASE", |rhs, lhs| {
1972 rhs.to_lowercase().cmp(&lhs.to_lowercase())
1973 })
1974 .unwrap();
1975
1976 crate::sql_query(
1977 "CREATE TABLE my_collation_example (id integer primary key autoincrement, value text collate RUSTNOCASE)",
1978 ).execute(connection)
1979 .unwrap();
1980 crate::sql_query(
1981 "INSERT INTO my_collation_example (value) VALUES ('foo'), ('FOo'), ('f00')",
1982 )
1983 .execute(connection)
1984 .unwrap();
1985
1986 let result = my_collation_example
1987 .filter(value.eq("foo"))
1988 .select(value)
1989 .load::<String>(connection);
1990 assert_eq!(
1991 Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
1992 result.as_ref().map(|vec| vec.as_ref())
1993 );
1994
1995 let result = my_collation_example
1996 .filter(value.eq("FOO"))
1997 .select(value)
1998 .load::<String>(connection);
1999 assert_eq!(
2000 Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
2001 result.as_ref().map(|vec| vec.as_ref())
2002 );
2003
2004 let result = my_collation_example
2005 .filter(value.eq("f00"))
2006 .select(value)
2007 .load::<String>(connection);
2008 assert_eq!(
2009 Ok(&["f00".to_owned()][..]),
2010 result.as_ref().map(|vec| vec.as_ref())
2011 );
2012
2013 let result = my_collation_example
2014 .filter(value.eq("F00"))
2015 .select(value)
2016 .load::<String>(connection);
2017 assert_eq!(
2018 Ok(&["f00".to_owned()][..]),
2019 result.as_ref().map(|vec| vec.as_ref())
2020 );
2021
2022 let result = my_collation_example
2023 .filter(value.eq("oof"))
2024 .select(value)
2025 .load::<String>(connection);
2026 assert_eq!(Ok(&[][..]), result.as_ref().map(|vec| vec.as_ref()));
2027 }
2028
2029 #[diesel_test_helper::test]
2031 fn test_correct_serialization_of_owned_strings() {
2032 use crate::prelude::*;
2033
2034 #[derive(Debug, crate::expression::AsExpression)]
2035 #[diesel(sql_type = diesel::sql_types::Text)]
2036 struct CustomWrapper(String);
2037
2038 impl crate::serialize::ToSql<Text, Sqlite> for CustomWrapper {
2039 fn to_sql<'b>(
2040 &'b self,
2041 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
2042 ) -> crate::serialize::Result {
2043 out.set_value(self.0.to_string());
2044 Ok(crate::serialize::IsNull::No)
2045 }
2046 }
2047
2048 let connection = &mut connection();
2049
2050 let res = crate::select(
2051 CustomWrapper("".into())
2052 .into_sql::<crate::sql_types::Text>()
2053 .nullable(),
2054 )
2055 .get_result::<Option<String>>(connection)
2056 .unwrap();
2057 assert_eq!(res, Some(String::new()));
2058 }
2059
2060 #[diesel_test_helper::test]
2061 fn test_correct_serialization_of_owned_bytes() {
2062 use crate::prelude::*;
2063
2064 #[derive(Debug, crate::expression::AsExpression)]
2065 #[diesel(sql_type = diesel::sql_types::Binary)]
2066 struct CustomWrapper(Vec<u8>);
2067
2068 impl crate::serialize::ToSql<crate::sql_types::Binary, Sqlite> for CustomWrapper {
2069 fn to_sql<'b>(
2070 &'b self,
2071 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
2072 ) -> crate::serialize::Result {
2073 out.set_value(self.0.clone());
2074 Ok(crate::serialize::IsNull::No)
2075 }
2076 }
2077
2078 let connection = &mut connection();
2079
2080 let res = crate::select(
2081 CustomWrapper(Vec::new())
2082 .into_sql::<crate::sql_types::Binary>()
2083 .nullable(),
2084 )
2085 .get_result::<Option<Vec<u8>>>(connection)
2086 .unwrap();
2087 assert_eq!(res, Some(Vec::new()));
2088 }
2089
2090 #[diesel_test_helper::test]
2091 fn correctly_handle_empty_query() {
2092 let check_empty_query_error = |r: crate::QueryResult<usize>| {
2093 assert!(r.is_err());
2094 let err = r.unwrap_err();
2095 assert!(
2096 matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::<crate::result::EmptyQuery>()),
2097 "Expected a query builder error, but got {err}"
2098 );
2099 };
2100 let connection = &mut SqliteConnection::establish(":memory:").unwrap();
2101 check_empty_query_error(crate::sql_query("").execute(connection));
2102 check_empty_query_error(crate::sql_query(" ").execute(connection));
2103 check_empty_query_error(crate::sql_query("\n\t").execute(connection));
2104 check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection));
2105 }
2106
2107 #[diesel_test_helper::test]
2108 fn last_insert_rowid_returns_none_on_fresh_connection() {
2109 let conn = &mut connection();
2110 assert_eq!(conn.last_insert_rowid(), None);
2111 }
2112
2113 #[diesel_test_helper::test]
2114 fn last_insert_rowid_returns_rowid_after_insert() {
2115 let conn = &mut connection();
2116 crate::sql_query("CREATE TABLE li_test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2117 .execute(conn)
2118 .unwrap();
2119
2120 crate::sql_query("INSERT INTO li_test (val) VALUES ('a')")
2121 .execute(conn)
2122 .unwrap();
2123 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2124
2125 crate::sql_query("INSERT INTO li_test (val) VALUES ('b')")
2126 .execute(conn)
2127 .unwrap();
2128 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(2));
2129 }
2130
2131 #[diesel_test_helper::test]
2132 fn last_insert_rowid_unchanged_after_failed_insert() {
2133 let conn = &mut connection();
2134 crate::sql_query(
2135 "CREATE TABLE li_test2 (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)",
2136 )
2137 .execute(conn)
2138 .unwrap();
2139
2140 crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')")
2141 .execute(conn)
2142 .unwrap();
2143 let rowid = conn.last_insert_rowid();
2144 assert_eq!(rowid, NonZeroI64::new(1));
2145
2146 let result = crate::sql_query("INSERT INTO li_test2 (val) VALUES ('a')").execute(conn);
2148 assert!(result.is_err());
2149
2150 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2152 }
2153
2154 #[diesel_test_helper::test]
2155 fn last_insert_rowid_with_explicit_rowid() {
2156 let conn = &mut connection();
2157 crate::sql_query("CREATE TABLE li_test3 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2158 .execute(conn)
2159 .unwrap();
2160
2161 crate::sql_query("INSERT INTO li_test3 (id, val) VALUES (42, 'a')")
2162 .execute(conn)
2163 .unwrap();
2164 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(42));
2165 }
2166
2167 #[diesel_test_helper::test]
2168 fn last_insert_rowid_unchanged_after_delete_and_update() {
2169 let conn = &mut connection();
2170 crate::sql_query("CREATE TABLE li_test4 (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
2171 .execute(conn)
2172 .unwrap();
2173
2174 crate::sql_query("INSERT INTO li_test4 (val) VALUES ('a')")
2175 .execute(conn)
2176 .unwrap();
2177 let rowid = conn.last_insert_rowid();
2178 assert_eq!(rowid, NonZeroI64::new(1));
2179
2180 crate::sql_query("UPDATE li_test4 SET val = 'b' WHERE id = 1")
2181 .execute(conn)
2182 .unwrap();
2183 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2184
2185 crate::sql_query("DELETE FROM li_test4 WHERE id = 1")
2186 .execute(conn)
2187 .unwrap();
2188 assert_eq!(conn.last_insert_rowid(), NonZeroI64::new(1));
2189 }
2190
2191 #[diesel_test_helper::test]
2192 fn read_bytes_from_blob() {
2193 table! {
2194 blobs {
2195 id -> Integer,
2196 data -> Blob,
2197 data2 -> Blob,
2198 }
2199 }
2200
2201 use std::io::Read;
2202
2203 let conn = &mut connection();
2204
2205 let _ =
2206 crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB, data2 BLOB)")
2207 .execute(conn);
2208
2209 let _ = crate::sql_query(
2210 "INSERT INTO blobs (data, data2) VALUES ('abc', 'def'), ('123', '456')",
2211 )
2212 .execute(conn);
2213
2214 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2215 let mut buf = vec![];
2216 data.read_to_end(&mut buf).unwrap();
2217
2218 assert_eq!(buf, b"abc");
2219
2220 let mut data2 = conn.get_read_only_blob(blobs::data2, 1).unwrap();
2221 let mut buf = vec![];
2222 data2.read_to_end(&mut buf).unwrap();
2223
2224 assert_eq!(buf, b"def");
2225 }
2226
2227 #[diesel_test_helper::test]
2228 fn read_seek_bytes() {
2229 table! {
2230 blobs {
2231 id -> Integer,
2232 data -> Blob,
2233 }
2234 }
2235
2236 use std::io::Read;
2237 use std::io::Seek;
2238 use std::io::SeekFrom;
2239
2240 let conn = &mut connection();
2241
2242 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2243 .execute(conn);
2244
2245 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abcdefghi')").execute(conn);
2246
2247 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2248
2249 let mut buf = [0; 1];
2250 assert_eq!(data.read(&mut buf).unwrap(), 1);
2251 assert_eq!(&buf, b"a");
2252
2253 assert_eq!(data.seek(SeekFrom::Current(1)).unwrap(), 2);
2255
2256 let mut buf = [0; 1];
2257 assert_eq!(data.read(&mut buf).unwrap(), 1);
2258 assert_eq!(&buf, b"c");
2259
2260 assert_eq!(data.seek(SeekFrom::Start(0)).unwrap(), 0);
2262
2263 let mut buf = [0; 1];
2264 assert_eq!(data.read(&mut buf).unwrap(), 1);
2265 assert_eq!(&buf, b"a");
2266
2267 assert_eq!(data.seek(SeekFrom::Current(-10)).unwrap(), 0);
2269
2270 let mut buf = [0; 1];
2271 assert_eq!(data.read(&mut buf).unwrap(), 1);
2272 assert_eq!(&buf, b"a");
2273
2274 data.seek(SeekFrom::Current(100)).unwrap();
2276
2277 let mut buf = [0; 1];
2279 assert_eq!(data.read(&mut buf).unwrap(), 0);
2280 }
2281
2282 #[diesel_test_helper::test]
2283 fn use_conn_after_blob_drop() {
2284 table! {
2285 blobs {
2286 id -> Integer,
2287 data -> Blob,
2288 }
2289 }
2290
2291 let conn = &mut connection();
2292
2293 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2294 .execute(conn);
2295
2296 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abc')").execute(conn);
2297
2298 let data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2299 drop(data);
2300
2301 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('def')").execute(conn);
2302 }
2303
2304 #[diesel_test_helper::test]
2305 fn blob_transaction() {
2306 table! {
2307 blobs {
2308 id -> Integer,
2309 data -> Blob,
2310 }
2311 }
2312
2313 use std::io::Read;
2314
2315 let conn = &mut connection();
2316
2317 let _ = crate::sql_query("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB)")
2318 .execute(conn);
2319
2320 let _ = crate::sql_query("INSERT INTO blobs (data) VALUES ('abc')").execute(conn);
2321
2322 {
2323 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2324 let mut buf = vec![];
2325 data.read_to_end(&mut buf).unwrap();
2326 assert_eq!(buf, b"abc");
2327 }
2328
2329 let res = conn.exclusive_transaction(|conn| {
2330 crate::sql_query("UPDATE blobs SET data = 'def' WHERE id = 1").execute(conn)?;
2331
2332 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2333 let mut buf = vec![];
2334 data.read_to_end(&mut buf).unwrap();
2335 assert_eq!(buf, b"def");
2336
2337 Result::<(), _>::Err(Error::RollbackTransaction)
2338 });
2339
2340 assert_eq!(res.unwrap_err(), Error::RollbackTransaction);
2341
2342 let mut data = conn.get_read_only_blob(blobs::data, 1).unwrap();
2343 let mut buf = vec![];
2344 data.read_to_end(&mut buf).unwrap();
2345 assert_eq!(buf, b"abc");
2346 }
2347
2348 #[diesel_test_helper::test]
2349 fn aggregate_function_works_with_aligned_data() {
2350 #[derive(Debug, Default)]
2351 #[repr(align(64))]
2352 struct OverAligned;
2353
2354 impl SqliteAggregateFunction<i32> for OverAligned {
2355 type Output = i64;
2356
2357 fn step(&mut self, _value: i32) {
2358 let need = core::mem::align_of::<Self>();
2359 let got = core::mem::align_of_val(self);
2360 assert_eq!(need, got);
2361 }
2362
2363 fn finalize(_agg: Option<Self>) -> i64 {
2364 0
2365 }
2366 }
2367 #[declare_sql_function]
2368 extern "SQL" {
2369 #[aggregate]
2370 fn over_aligned_sum(x: Integer) -> diesel::sql_types::BigInt;
2371 }
2372
2373 let mut conn = SqliteConnection::establish(":memory:").unwrap();
2374 over_aligned_sum_utils::register_impl::<OverAligned, _>(&mut conn).unwrap();
2375
2376 diesel::select(over_aligned_sum(1))
2377 .execute(&mut conn)
2378 .unwrap();
2379 }
2380
2381 #[diesel_test_helper::test]
2382 fn sum_twice() {
2383 #[derive(Default)]
2384 struct Sum(i32);
2385
2386 impl SqliteAggregateFunction<i32> for Sum {
2387 type Output = i32;
2388
2389 fn step(&mut self, value: i32) {
2390 self.0 += value;
2391 }
2392
2393 fn finalize(agg: Option<Self>) -> i32 {
2394 agg.map(|s| s.0).unwrap_or_default()
2395 }
2396 }
2397
2398 #[declare_sql_function]
2399 extern "SQL" {
2400 #[aggregate]
2401 fn my_sum(x: Integer) -> Integer;
2402 }
2403
2404 let mut conn = SqliteConnection::establish(":memory:").unwrap();
2405 my_sum_utils::register_impl::<Sum, _>(&mut conn).unwrap();
2406
2407 conn.batch_execute(
2408 "
2409 CREATE TABLE test(key1 INTEGER, key2 INTEGER);
2410 INSERT INTO test(key1, key2) VALUES (1, 2), (2, 4), (3, 6);
2411",
2412 )
2413 .unwrap();
2414
2415 table! {
2416 test (key1, key2) {
2417 key1 -> Integer,
2418 key2 -> Integer,
2419 }
2420 }
2421
2422 let (first_res, second_res) = test::table
2423 .select((my_sum(test::key1), my_sum(test::key2)))
2424 .get_result::<(i32, i32)>(&mut conn)
2425 .unwrap();
2426
2427 assert_eq!(first_res, 6);
2428 assert_eq!(second_res, 12);
2429
2430 conn.batch_execute("DELETE FROM test").unwrap();
2431 let (first_res, second_res) = test::table
2432 .select((my_sum(test::key1), my_sum(test::key2)))
2433 .get_result::<(i32, i32)>(&mut conn)
2434 .unwrap();
2435
2436 assert_eq!(first_res, 0);
2437 assert_eq!(second_res, 0);
2438 }
2439
2440 #[diesel_test_helper::test]
2441 fn test_injection() {
2442 diesel::table! {
2443 #[sql_name = "quote'table"]
2444 quote_table (id) {
2445 id -> Nullable<Integer>,
2446 name -> Nullable<Text>,
2447 }
2448 }
2449
2450 let mut conn = SqliteConnection::establish(":memory:").unwrap();
2451
2452 conn.batch_execute("CREATE TABLE \"quote'table\" (id INTEGER PRIMARY KEY, name TEXT);")
2453 .unwrap();
2454
2455 diesel::insert_into(quote_table::table)
2456 .values((quote_table::id.eq(1), quote_table::name.eq("Jane")))
2457 .execute(&mut conn)
2458 .unwrap();
2459
2460 let data = quote_table::table
2461 .load::<(Option<i32>, Option<String>)>(&mut conn)
2462 .unwrap();
2463 assert_eq!(data, [(Some(1), Some("Jane".to_owned()))]);
2464 }
2465
2466 #[diesel_test_helper::test]
2467 fn set_limit_returns_previous_value() {
2468 let mut conn = connection();
2469 let original = conn.get_limit(SqliteLimit::SqlLength);
2470
2471 assert_eq!(conn.set_limit(SqliteLimit::SqlLength, 1024), original);
2474 assert_eq!(conn.set_limit(SqliteLimit::SqlLength, 2048), 1024);
2475 assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 2048);
2476 }
2477
2478 #[diesel_test_helper::test]
2479 fn get_limit_does_not_mutate() {
2480 let conn = connection();
2481 let first = conn.get_limit(SqliteLimit::ExprDepth);
2482 assert!(first > 0);
2485 assert_eq!(conn.get_limit(SqliteLimit::ExprDepth), first);
2486 }
2487
2488 #[diesel_test_helper::test]
2489 fn set_limit_enforces_length() {
2490 let mut conn = connection();
2491 conn.set_limit(SqliteLimit::Length, 100);
2492
2493 assert!(
2494 crate::sql_query("SELECT length(randomblob(50))")
2495 .execute(&mut conn)
2496 .is_ok()
2497 );
2498 assert!(
2500 crate::sql_query("SELECT length(randomblob(500))")
2501 .execute(&mut conn)
2502 .is_err()
2503 );
2504 }
2505
2506 #[diesel_test_helper::test]
2507 fn set_limit_enforces_column_count() {
2508 let wide = format!(
2511 "SELECT {}",
2512 (1..=30)
2513 .map(|i| i.to_string())
2514 .collect::<Vec<_>>()
2515 .join(", ")
2516 );
2517
2518 let mut unconstrained = connection();
2519 assert!(crate::sql_query(&wide).execute(&mut unconstrained).is_ok());
2520
2521 let mut conn = connection();
2522 conn.set_limit(SqliteLimit::ColumnCount, 10);
2523 assert!(crate::sql_query(&wide).execute(&mut conn).is_err());
2524 }
2525
2526 #[diesel_test_helper::test]
2527 fn set_limit_enforces_expr_depth() {
2528 let mut conn = connection();
2529 conn.set_limit(SqliteLimit::ExprDepth, 5);
2530
2531 assert!(crate::sql_query("SELECT 1+1").execute(&mut conn).is_ok());
2532 let deep = format!("SELECT {}1", "1+".repeat(40));
2534 assert!(crate::sql_query(&deep).execute(&mut conn).is_err());
2535 }
2536
2537 #[diesel_test_helper::test]
2538 fn set_limit_enforces_compound_select() {
2539 let mut conn = connection();
2540 conn.set_limit(SqliteLimit::CompoundSelect, 2);
2541
2542 assert!(
2543 crate::sql_query("SELECT 1 UNION SELECT 2")
2544 .execute(&mut conn)
2545 .is_ok()
2546 );
2547 assert!(
2549 crate::sql_query(
2550 "SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5"
2551 )
2552 .execute(&mut conn)
2553 .is_err()
2554 );
2555 }
2556
2557 #[diesel_test_helper::test]
2558 fn set_limit_enforces_vdbe_op() {
2559 let heavy = "SELECT count(*) FROM sqlite_master a, sqlite_master b, sqlite_master c";
2562
2563 let mut unconstrained = connection();
2564 assert!(crate::sql_query(heavy).execute(&mut unconstrained).is_ok());
2565
2566 let mut conn = connection();
2567 conn.set_limit(SqliteLimit::VdbeOp, 5);
2568 assert!(crate::sql_query(heavy).execute(&mut conn).is_err());
2569 }
2570
2571 #[diesel_test_helper::test]
2572 fn set_limit_enforces_function_arg() {
2573 let mut conn = connection();
2574 conn.set_limit(SqliteLimit::FunctionArg, 3);
2575
2576 assert!(
2577 crate::sql_query("SELECT max(1, 2, 3)")
2578 .execute(&mut conn)
2579 .is_ok()
2580 );
2581 assert!(
2583 crate::sql_query("SELECT max(1, 2, 3, 4, 5, 6, 7, 8)")
2584 .execute(&mut conn)
2585 .is_err()
2586 );
2587 }
2588
2589 #[diesel_test_helper::test]
2590 fn set_limit_enforces_attached() {
2591 let mut conn = connection();
2592 conn.set_limit(SqliteLimit::Attached, 0);
2593
2594 assert!(
2596 crate::sql_query("ATTACH DATABASE ':memory:' AS aux_db")
2597 .execute(&mut conn)
2598 .is_err()
2599 );
2600 }
2601
2602 #[diesel_test_helper::test]
2603 fn set_limit_enforces_variable_number() {
2604 let mut conn = connection();
2605 conn.set_limit(
2609 SqliteLimit::VariableNumber,
2610 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT,
2611 );
2612 let at_limit = format!("SELECT ?{}", SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT);
2613 let past_limit = format!(
2614 "SELECT ?{}",
2615 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT as i64 + 1
2616 );
2617 assert!(crate::sql_query(&at_limit).execute(&mut conn).is_ok());
2618 assert!(crate::sql_query(&past_limit).execute(&mut conn).is_err());
2619 }
2620
2621 #[diesel_test_helper::test]
2622 fn set_limit_enforces_trigger_depth() {
2623 use crate::connection::SimpleConnection;
2624
2625 let setup = "PRAGMA recursive_triggers = ON;\
2627 CREATE TABLE recur (x INTEGER);\
2628 CREATE TRIGGER recur_tr AFTER INSERT ON recur WHEN NEW.x < 100 \
2629 BEGIN INSERT INTO recur VALUES (NEW.x + 1); END;";
2630
2631 let mut unconstrained = connection();
2633 unconstrained.batch_execute(setup).unwrap();
2634 assert!(
2635 crate::sql_query("INSERT INTO recur VALUES (1)")
2636 .execute(&mut unconstrained)
2637 .is_ok()
2638 );
2639
2640 let mut conn = connection();
2643 conn.set_limit(SqliteLimit::TriggerDepth, 3);
2644 conn.batch_execute(setup).unwrap();
2645 assert!(
2646 crate::sql_query("INSERT INTO recur VALUES (1)")
2647 .execute(&mut conn)
2648 .is_err()
2649 );
2650 }
2651
2652 #[diesel_test_helper::test]
2653 fn worker_threads_limit_has_no_runtime_error_path() {
2654 let mut conn = connection();
2659 conn.set_limit(SqliteLimit::WorkerThreads, 0);
2660 assert_eq!(conn.get_limit(SqliteLimit::WorkerThreads), 0);
2661 assert!(crate::sql_query("SELECT 1").execute(&mut conn).is_ok());
2662 }
2663
2664 #[diesel_test_helper::test]
2665 fn set_limit_enforces_sql_length() {
2666 let mut conn = connection();
2667 conn.set_limit(SqliteLimit::SqlLength, 20);
2668
2669 let result =
2671 crate::sql_query("SELECT * FROM sqlite_master WHERE type = 'table'").execute(&mut conn);
2672 assert!(result.is_err());
2673 }
2674
2675 #[diesel_test_helper::test]
2676 fn set_limit_enforces_like_pattern_length() {
2677 let mut conn = connection();
2678 conn.set_limit(SqliteLimit::LikePatternLength, 100);
2679
2680 assert!(
2681 crate::sql_query("SELECT 'test' LIKE 'te%'")
2682 .execute(&mut conn)
2683 .is_ok()
2684 );
2685
2686 let long_pattern = "%".repeat(200);
2687 let query = format!("SELECT 'test' LIKE '{long_pattern}'");
2688 assert!(crate::sql_query(&query).execute(&mut conn).is_err());
2689 }
2690
2691 #[diesel_test_helper::test]
2692 fn set_limit_clamps_above_compile_time_maximum() {
2693 let mut conn = connection();
2694 conn.set_limit(SqliteLimit::Length, i32::MAX);
2697 let clamped = conn.get_limit(SqliteLimit::Length);
2698 assert!(clamped > 0 && clamped < i32::MAX);
2699 }
2700
2701 #[diesel_test_helper::test]
2702 fn set_recommended_security_limits_applies_documented_table() {
2703 let mut conn = connection();
2704 conn.set_recommended_security_limits();
2705
2706 assert_eq!(conn.get_limit(SqliteLimit::Length), 1_000_000);
2707 assert_eq!(conn.get_limit(SqliteLimit::SqlLength), 100_000);
2708 assert_eq!(conn.get_limit(SqliteLimit::ColumnCount), 100);
2709 assert_eq!(conn.get_limit(SqliteLimit::ExprDepth), 10);
2710 assert_eq!(conn.get_limit(SqliteLimit::CompoundSelect), 3);
2711 assert_eq!(conn.get_limit(SqliteLimit::VdbeOp), 25_000);
2712 assert_eq!(conn.get_limit(SqliteLimit::FunctionArg), 8);
2713 assert_eq!(conn.get_limit(SqliteLimit::Attached), 0);
2714 assert_eq!(conn.get_limit(SqliteLimit::LikePatternLength), 50);
2715 assert_eq!(conn.get_limit(SqliteLimit::VariableNumber), 10);
2716 assert_eq!(conn.get_limit(SqliteLimit::TriggerDepth), 10);
2717 }
2718
2719 #[diesel_test_helper::test]
2720 fn safe_limit_constants_do_not_exceed_defaults() {
2721 let pairs = [
2727 (
2728 SqliteLimit::SAFE_LENGTH_LIMIT,
2729 SqliteLimit::DEFAULT_LENGTH_LIMIT,
2730 ),
2731 (
2732 SqliteLimit::SAFE_SQL_LENGTH_LIMIT,
2733 SqliteLimit::DEFAULT_SQL_LENGTH_LIMIT,
2734 ),
2735 (
2736 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT,
2737 SqliteLimit::DEFAULT_COLUMN_COUNT_LIMIT,
2738 ),
2739 (
2740 SqliteLimit::SAFE_EXPR_DEPTH_LIMIT,
2741 SqliteLimit::DEFAULT_EXPR_DEPTH_LIMIT,
2742 ),
2743 (
2744 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT,
2745 SqliteLimit::DEFAULT_COMPOUND_SELECT_LIMIT,
2746 ),
2747 (
2748 SqliteLimit::SAFE_VDBE_OP_LIMIT,
2749 SqliteLimit::DEFAULT_VDBE_OP_LIMIT,
2750 ),
2751 (
2752 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT,
2753 SqliteLimit::DEFAULT_FUNCTION_ARG_LIMIT,
2754 ),
2755 (
2756 SqliteLimit::SAFE_ATTACHED_LIMIT,
2757 SqliteLimit::DEFAULT_ATTACHED_LIMIT,
2758 ),
2759 (
2760 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT,
2761 SqliteLimit::DEFAULT_LIKE_PATTERN_LENGTH_LIMIT,
2762 ),
2763 (
2764 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT,
2765 SqliteLimit::DEFAULT_VARIABLE_NUMBER_LIMIT,
2766 ),
2767 (
2768 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT,
2769 SqliteLimit::DEFAULT_TRIGGER_DEPTH_LIMIT,
2770 ),
2771 (
2772 SqliteLimit::SAFE_WORKER_THREADS_LIMIT,
2773 SqliteLimit::DEFAULT_WORKER_THREADS_LIMIT,
2774 ),
2775 ];
2776 for (safe, default) in pairs {
2777 assert!(
2778 safe <= default,
2779 "safe value {safe} exceeds default {default}"
2780 );
2781 }
2782 }
2783
2784 #[diesel_test_helper::test]
2785 fn safe_limit_constants_match_recommended_setter() {
2786 let mut conn = connection();
2787 conn.set_recommended_security_limits();
2788
2789 assert_eq!(
2790 conn.get_limit(SqliteLimit::Length),
2791 SqliteLimit::SAFE_LENGTH_LIMIT
2792 );
2793 assert_eq!(
2794 conn.get_limit(SqliteLimit::SqlLength),
2795 SqliteLimit::SAFE_SQL_LENGTH_LIMIT
2796 );
2797 assert_eq!(
2798 conn.get_limit(SqliteLimit::ColumnCount),
2799 SqliteLimit::SAFE_COLUMN_COUNT_LIMIT
2800 );
2801 assert_eq!(
2802 conn.get_limit(SqliteLimit::ExprDepth),
2803 SqliteLimit::SAFE_EXPR_DEPTH_LIMIT
2804 );
2805 assert_eq!(
2806 conn.get_limit(SqliteLimit::CompoundSelect),
2807 SqliteLimit::SAFE_COMPOUND_SELECT_LIMIT
2808 );
2809 assert_eq!(
2810 conn.get_limit(SqliteLimit::VdbeOp),
2811 SqliteLimit::SAFE_VDBE_OP_LIMIT
2812 );
2813 assert_eq!(
2814 conn.get_limit(SqliteLimit::FunctionArg),
2815 SqliteLimit::SAFE_FUNCTION_ARG_LIMIT
2816 );
2817 assert_eq!(
2818 conn.get_limit(SqliteLimit::Attached),
2819 SqliteLimit::SAFE_ATTACHED_LIMIT
2820 );
2821 assert_eq!(
2822 conn.get_limit(SqliteLimit::LikePatternLength),
2823 SqliteLimit::SAFE_LIKE_PATTERN_LENGTH_LIMIT
2824 );
2825 assert_eq!(
2826 conn.get_limit(SqliteLimit::VariableNumber),
2827 SqliteLimit::SAFE_VARIABLE_NUMBER_LIMIT
2828 );
2829 assert_eq!(
2830 conn.get_limit(SqliteLimit::TriggerDepth),
2831 SqliteLimit::SAFE_TRIGGER_DEPTH_LIMIT
2832 );
2833 assert_eq!(
2836 conn.get_limit(SqliteLimit::WorkerThreads),
2837 SqliteLimit::SAFE_WORKER_THREADS_LIMIT
2838 );
2839 }
2840
2841 #[diesel_test_helper::test]
2844 fn db_config_defensive_roundtrip() {
2845 let conn = &mut connection();
2846 conn.set_defensive(true).unwrap();
2847 assert!(conn.is_defensive().unwrap());
2848 conn.set_defensive(false).unwrap();
2849 assert!(!conn.is_defensive().unwrap());
2850 }
2851
2852 #[diesel_test_helper::test]
2853 fn db_config_trusted_schema_roundtrip() {
2854 let conn = &mut connection();
2855 conn.set_trusted_schema(false).unwrap();
2856 assert!(!conn.is_trusted_schema().unwrap());
2857 conn.set_trusted_schema(true).unwrap();
2858 assert!(conn.is_trusted_schema().unwrap());
2859 }
2860
2861 #[diesel_test_helper::test]
2862 fn db_config_with_load_extension_enabled_scopes_the_flag() {
2863 let conn = &mut connection();
2864 conn.with_load_extension_enabled(|conn| {
2865 assert!(conn.is_load_extension_enabled().unwrap());
2867 QueryResult::Ok(())
2868 })
2869 .unwrap();
2870 assert!(!conn.is_load_extension_enabled().unwrap());
2872 }
2873
2874 #[cfg(all(
2875 feature = "std",
2876 not(all(target_family = "wasm", target_os = "unknown"))
2877 ))]
2878 #[diesel_test_helper::test]
2879 fn with_load_extension_enabled_disables_after_panic() {
2880 let conn = &mut connection();
2881 let outcome = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
2882 conn.with_load_extension_enabled(|_conn| -> QueryResult<()> {
2883 panic!("boom inside closure");
2884 })
2885 }));
2886 assert!(outcome.is_err(), "panic should propagate");
2887 assert!(
2888 !conn.is_load_extension_enabled().unwrap(),
2889 "extension loading must be disabled again after a panic"
2890 );
2891 }
2892
2893 #[diesel_test_helper::test]
2894 fn db_config_triggers_roundtrip() {
2895 let conn = &mut connection();
2896 conn.set_triggers_enabled(false).unwrap();
2897 assert!(!conn.are_triggers_enabled().unwrap());
2898 conn.set_triggers_enabled(true).unwrap();
2899 assert!(conn.are_triggers_enabled().unwrap());
2900 }
2901
2902 #[diesel_test_helper::test]
2903 fn db_config_views_roundtrip() {
2904 let conn = &mut connection();
2905 conn.set_views_enabled(false).unwrap();
2906 assert!(!conn.are_views_enabled().unwrap());
2907 conn.set_views_enabled(true).unwrap();
2908 assert!(conn.are_views_enabled().unwrap());
2909 }
2910
2911 #[diesel_test_helper::test]
2912 fn db_config_foreign_keys_roundtrip() {
2913 let conn = &mut connection();
2914 conn.set_foreign_keys_enabled(true).unwrap();
2915 assert!(conn.are_foreign_keys_enabled().unwrap());
2916 conn.set_foreign_keys_enabled(false).unwrap();
2917 assert!(!conn.are_foreign_keys_enabled().unwrap());
2918 }
2919
2920 #[diesel_test_helper::test]
2921 fn db_config_dqs_dml_roundtrip() {
2922 let conn = &mut connection();
2923 conn.set_double_quoted_strings_dml(false).unwrap();
2924 assert!(!conn.are_double_quoted_strings_dml_enabled().unwrap());
2925 conn.set_double_quoted_strings_dml(true).unwrap();
2926 assert!(conn.are_double_quoted_strings_dml_enabled().unwrap());
2927 }
2928
2929 #[diesel_test_helper::test]
2930 fn db_config_dqs_ddl_roundtrip() {
2931 let conn = &mut connection();
2932 conn.set_double_quoted_strings_ddl(false).unwrap();
2933 assert!(!conn.are_double_quoted_strings_ddl_enabled().unwrap());
2934 conn.set_double_quoted_strings_ddl(true).unwrap();
2935 assert!(conn.are_double_quoted_strings_ddl_enabled().unwrap());
2936 }
2937
2938 #[diesel_test_helper::test]
2939 fn db_config_fts3_tokenizer_roundtrip() {
2940 let conn = &mut connection();
2941 conn.set_fts3_tokenizer_enabled(false).unwrap();
2942 assert!(!conn.is_fts3_tokenizer_enabled().unwrap());
2943 conn.set_fts3_tokenizer_enabled(true).unwrap();
2944 assert!(conn.is_fts3_tokenizer_enabled().unwrap());
2945 }
2946
2947 #[diesel_test_helper::test]
2948 fn db_config_writable_schema_roundtrip() {
2949 let conn = &mut connection();
2950 conn.set_writable_schema(false).unwrap();
2951 assert!(!conn.is_writable_schema().unwrap());
2952 conn.set_writable_schema(true).unwrap();
2953 assert!(conn.is_writable_schema().unwrap());
2954 }
2955
2956 #[diesel_test_helper::test]
2957 fn db_config_attach_create_roundtrip() {
2958 let conn = &mut connection();
2959 if conn.set_attach_create_enabled(false).is_err() {
2961 return;
2962 }
2963 assert!(!conn.is_attach_create_enabled().unwrap());
2964 conn.set_attach_create_enabled(true).unwrap();
2965 assert!(conn.is_attach_create_enabled().unwrap());
2966 }
2967
2968 #[diesel_test_helper::test]
2969 fn db_config_attach_write_roundtrip() {
2970 let conn = &mut connection();
2971 if conn.set_attach_write_enabled(false).is_err() {
2973 return;
2974 }
2975 assert!(!conn.is_attach_write_enabled().unwrap());
2976 conn.set_attach_write_enabled(true).unwrap();
2977 assert!(conn.is_attach_write_enabled().unwrap());
2978 }
2979
2980 #[diesel_test_helper::test]
2983 fn defensive_mode_blocks_writable_schema() {
2984 let conn = &mut connection();
2985 conn.set_defensive(true).unwrap();
2986 let _ = crate::sql_query("PRAGMA writable_schema = ON").execute(conn);
2988 assert!(!conn.is_writable_schema().unwrap());
2989 }
2990
2991 #[diesel_test_helper::test]
2992 fn foreign_keys_enabled_enforces_constraints() {
2993 let conn = &mut connection();
2994 conn.set_foreign_keys_enabled(true).unwrap();
2995
2996 crate::sql_query("CREATE TABLE parent (id INTEGER PRIMARY KEY)")
2997 .execute(conn)
2998 .unwrap();
2999 crate::sql_query(
3000 "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
3001 )
3002 .execute(conn)
3003 .unwrap();
3004
3005 let result =
3007 crate::sql_query("INSERT INTO child (id, parent_id) VALUES (1, 999)").execute(conn);
3008 assert!(result.is_err());
3009 }
3010
3011 #[diesel_test_helper::test]
3012 fn views_disabled_blocks_view_queries() {
3013 let conn = &mut connection();
3014 crate::sql_query("CREATE TABLE base (id INTEGER PRIMARY KEY)")
3015 .execute(conn)
3016 .unwrap();
3017 crate::sql_query("INSERT INTO base (id) VALUES (1)")
3018 .execute(conn)
3019 .unwrap();
3020 crate::sql_query("CREATE VIEW base_view AS SELECT id FROM base")
3021 .execute(conn)
3022 .unwrap();
3023
3024 conn.set_views_enabled(true).unwrap();
3026 assert!(
3027 crate::sql_query("SELECT id FROM base_view")
3028 .execute(conn)
3029 .is_ok()
3030 );
3031
3032 conn.set_views_enabled(false).unwrap();
3034 assert!(
3035 crate::sql_query("SELECT id FROM base_view")
3036 .execute(conn)
3037 .is_err()
3038 );
3039 }
3040
3041 #[diesel_test_helper::test]
3042 fn triggers_disabled_prevents_firing() {
3043 let conn = &mut connection();
3044 crate::sql_query("CREATE TABLE source (id INTEGER PRIMARY KEY)")
3045 .execute(conn)
3046 .unwrap();
3047 crate::sql_query("CREATE TABLE trigger_log (n INTEGER)")
3048 .execute(conn)
3049 .unwrap();
3050 crate::sql_query("CREATE TRIGGER log_insert AFTER INSERT ON source BEGIN INSERT INTO trigger_log (n) VALUES (1); END")
3051 .execute(conn)
3052 .unwrap();
3053
3054 conn.set_triggers_enabled(false).unwrap();
3056 crate::sql_query("INSERT INTO source (id) VALUES (1)")
3057 .execute(conn)
3058 .unwrap();
3059 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
3060 .get_result(conn)
3061 .unwrap();
3062 assert_eq!(0, count, "trigger should not fire while disabled");
3063
3064 conn.set_triggers_enabled(true).unwrap();
3066 crate::sql_query("INSERT INTO source (id) VALUES (2)")
3067 .execute(conn)
3068 .unwrap();
3069 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
3070 .get_result(conn)
3071 .unwrap();
3072 assert_eq!(1, count, "trigger should fire while enabled");
3073 }
3074
3075 #[diesel_test_helper::test]
3076 fn dqs_dml_controls_double_quoted_string_literals() {
3077 let conn = &mut connection();
3078
3079 conn.set_double_quoted_strings_dml(false).unwrap();
3082 let disabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
3083 assert!(disabled.is_err());
3084
3085 conn.set_double_quoted_strings_dml(true).unwrap();
3087 let enabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
3088 assert_eq!(Ok("bare_token".to_owned()), enabled);
3089 }
3090
3091 #[diesel_test_helper::test]
3092 fn dqs_ddl_controls_double_quoted_string_literals() {
3093 let conn = &mut connection();
3094
3095 conn.set_double_quoted_strings_ddl(false).unwrap();
3098 let disabled =
3099 crate::sql_query(r#"CREATE TABLE dqs_off (name TEXT, CHECK (name <> "not_a_column"))"#)
3100 .execute(conn);
3101 assert!(disabled.is_err());
3102
3103 conn.set_double_quoted_strings_ddl(true).unwrap();
3106 let enabled =
3107 crate::sql_query(r#"CREATE TABLE dqs_on (name TEXT, CHECK (name <> "not_a_column"))"#)
3108 .execute(conn);
3109 assert!(enabled.is_ok());
3110 }
3111
3112 #[diesel_test_helper::test]
3113 fn writable_schema_controls_direct_sqlite_master_writes() {
3114 let conn = &mut connection();
3115 crate::sql_query("CREATE TABLE protected (id INTEGER PRIMARY KEY)")
3116 .execute(conn)
3117 .unwrap();
3118
3119 let update =
3120 "UPDATE sqlite_master SET sql = sql WHERE type = 'table' AND name = 'protected'";
3121
3122 conn.set_writable_schema(false).unwrap();
3124 assert!(crate::sql_query(update).execute(conn).is_err());
3125
3126 conn.set_writable_schema(true).unwrap();
3128 assert!(crate::sql_query(update).execute(conn).is_ok());
3129 }
3130
3131 #[diesel_test_helper::test]
3132 fn fts3_tokenizer_disabled_blocks_the_function() {
3133 let conn = &mut connection();
3134
3135 conn.set_fts3_tokenizer_enabled(true).unwrap();
3137 let enabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
3138 .get_result::<Vec<u8>>(conn);
3139 if enabled.is_err() {
3140 return;
3142 }
3143
3144 conn.set_fts3_tokenizer_enabled(false).unwrap();
3146 let disabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
3147 .get_result::<Vec<u8>>(conn);
3148 assert!(disabled.is_err());
3149 }
3150
3151 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3154 fn temp_db_path(tag: &str) -> std::path::PathBuf {
3155 let mut path = std::env::temp_dir();
3156 path.push(format!("diesel_attach_{}_{}.db", std::process::id(), tag));
3157 path
3158 }
3159
3160 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3161 #[diesel_test_helper::test]
3162 fn attach_create_disabled_blocks_new_database_files() {
3163 let conn = &mut connection();
3164
3165 if conn.set_attach_create_enabled(false).is_err() {
3168 return;
3169 }
3170
3171 let path = temp_db_path("create");
3172 let _ = std::fs::remove_file(&path);
3173 let attach = format!("ATTACH DATABASE '{}' AS aux_create", path.display());
3174
3175 assert!(crate::sql_query(&attach).execute(conn).is_err());
3177
3178 conn.set_attach_create_enabled(true).unwrap();
3180 crate::sql_query(&attach).execute(conn).unwrap();
3181 crate::sql_query("DETACH DATABASE aux_create")
3182 .execute(conn)
3183 .unwrap();
3184
3185 let _ = std::fs::remove_file(&path);
3186 }
3187
3188 #[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
3189 #[diesel_test_helper::test]
3190 fn attach_write_disabled_opens_attached_databases_read_only() {
3191 let conn = &mut connection();
3192
3193 if conn.set_attach_write_enabled(false).is_err() {
3197 return;
3198 }
3199
3200 let path = temp_db_path("write");
3202 let _ = std::fs::remove_file(&path);
3203 {
3204 let mut seed = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
3205 crate::sql_query("CREATE TABLE t (id INTEGER)")
3206 .execute(&mut seed)
3207 .unwrap();
3208 }
3209 let attach = format!("ATTACH DATABASE '{}' AS aux_write", path.display());
3210
3211 crate::sql_query(&attach).execute(conn).unwrap();
3213 assert!(
3214 crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
3215 .execute(conn)
3216 .is_err()
3217 );
3218 crate::sql_query("DETACH DATABASE aux_write")
3219 .execute(conn)
3220 .unwrap();
3221
3222 conn.set_attach_write_enabled(true).unwrap();
3224 crate::sql_query(&attach).execute(conn).unwrap();
3225 crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
3226 .execute(conn)
3227 .unwrap();
3228 crate::sql_query("DETACH DATABASE aux_write")
3229 .execute(conn)
3230 .unwrap();
3231
3232 let _ = std::fs::remove_file(&path);
3233 }
3234
3235 #[declare_sql_function]
3238 extern "SQL" {
3239 fn directonly_fn() -> Integer;
3240 fn innocuous_fn() -> Integer;
3241 }
3242
3243 #[diesel_test_helper::test]
3244 fn directonly_function_blocked_from_view() {
3245 let conn = &mut connection();
3246
3247 directonly_fn_utils::register_impl_with_behavior(
3249 conn,
3250 SqliteFunctionBehavior::DIRECTONLY,
3251 || 42,
3252 )
3253 .unwrap();
3254
3255 let result = crate::select(directonly_fn()).get_result::<i32>(conn);
3257 assert_eq!(Ok(42), result);
3258
3259 crate::sql_query("CREATE VIEW test_view AS SELECT directonly_fn() AS val")
3261 .execute(conn)
3262 .unwrap();
3263
3264 conn.set_trusted_schema(false).unwrap();
3266
3267 let result = crate::sql_query("SELECT val FROM test_view").execute(conn);
3269 assert!(result.is_err());
3270 }
3271
3272 #[diesel_test_helper::test]
3273 fn innocuous_function_allowed_from_view_with_untrusted_schema() {
3274 let conn = &mut connection();
3275
3276 innocuous_fn_utils::register_impl_with_behavior(
3278 conn,
3279 SqliteFunctionBehavior::DETERMINISTIC | SqliteFunctionBehavior::INNOCUOUS,
3280 || 99,
3281 )
3282 .unwrap();
3283
3284 crate::sql_query("CREATE VIEW innocuous_view AS SELECT innocuous_fn() AS val")
3286 .execute(conn)
3287 .unwrap();
3288
3289 conn.set_trusted_schema(false).unwrap();
3291
3292 let result = crate::sql_query("SELECT val FROM innocuous_view").execute(conn);
3294 assert!(result.is_ok());
3295 }
3296}