diesel/sqlite/connection/
db_config.rs1#[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
7use super::SqliteConnection;
8use super::raw;
9use crate::result::QueryResult;
10
11impl SqliteConnection {
12 pub fn set_defensive(&mut self, enabled: bool) -> QueryResult<()> {
39 self.raw_connection
40 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE, enabled)
41 }
42
43 pub fn is_defensive(&self) -> QueryResult<bool> {
47 self.raw_connection
48 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DEFENSIVE)
49 }
50
51 pub fn set_trusted_schema(&mut self, trusted: bool) -> QueryResult<()> {
63 self.raw_connection
64 .set_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA, trusted)
65 }
66
67 pub fn is_trusted_schema(&self) -> QueryResult<bool> {
71 self.raw_connection
72 .get_db_config_bool(ffi::SQLITE_DBCONFIG_TRUSTED_SCHEMA)
73 }
74
75 pub fn with_load_extension_enabled<R, E>(
103 &mut self,
104 f: impl FnOnce(&mut Self) -> Result<R, E>,
105 ) -> Result<R, E>
106 where
107 E: From<crate::result::Error>,
108 {
109 self.set_load_extension_enabled(true)?;
110
111 #[cfg(feature = "std")]
115 {
116 match std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| f(self))) {
117 Ok(r) => {
118 self.set_load_extension_enabled(false)?;
119 r
120 }
121 Err(panic) => {
122 let _ = self.set_load_extension_enabled(false);
123 std::panic::resume_unwind(panic);
124 }
125 }
126 }
127 #[cfg(not(feature = "std"))]
128 {
129 let r = f(self);
130 self.set_load_extension_enabled(false)?;
131 r
132 }
133 }
134
135 fn set_load_extension_enabled(&mut self, enabled: bool) -> QueryResult<()> {
136 self.raw_connection
137 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, enabled)
138 }
139
140 #[cfg(test)]
141 fn is_load_extension_enabled(&self) -> QueryResult<bool> {
142 self.raw_connection
143 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION)
144 }
145
146 pub fn set_fts3_tokenizer_enabled(&mut self, enabled: bool) -> QueryResult<()> {
155 self.raw_connection
156 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, enabled)
157 }
158
159 pub fn is_fts3_tokenizer_enabled(&self) -> QueryResult<bool> {
163 self.raw_connection
164 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER)
165 }
166
167 pub fn set_writable_schema(&mut self, enabled: bool) -> QueryResult<()> {
176 self.raw_connection
177 .set_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA, enabled)
178 }
179
180 pub fn is_writable_schema(&self) -> QueryResult<bool> {
184 self.raw_connection
185 .get_db_config_bool(ffi::SQLITE_DBCONFIG_WRITABLE_SCHEMA)
186 }
187
188 pub fn set_attach_create_enabled(&mut self, enabled: bool) -> QueryResult<()> {
196 self.raw_connection
197 .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, enabled)
198 }
199
200 pub fn is_attach_create_enabled(&self) -> QueryResult<bool> {
204 self.raw_connection
205 .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE)
206 }
207
208 pub fn set_attach_write_enabled(&mut self, enabled: bool) -> QueryResult<()> {
215 self.raw_connection
216 .set_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, enabled)
217 }
218
219 pub fn is_attach_write_enabled(&self) -> QueryResult<bool> {
223 self.raw_connection
224 .get_db_config_bool(raw::SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE)
225 }
226
227 pub fn set_triggers_enabled(&mut self, enabled: bool) -> QueryResult<()> {
233 self.raw_connection
234 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER, enabled)
235 }
236
237 pub fn are_triggers_enabled(&self) -> QueryResult<bool> {
241 self.raw_connection
242 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER)
243 }
244
245 pub fn set_views_enabled(&mut self, enabled: bool) -> QueryResult<()> {
251 self.raw_connection
252 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW, enabled)
253 }
254
255 pub fn are_views_enabled(&self) -> QueryResult<bool> {
259 self.raw_connection
260 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_VIEW)
261 }
262
263 pub fn set_foreign_keys_enabled(&mut self, enabled: bool) -> QueryResult<()> {
269 self.raw_connection
270 .set_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY, enabled)
271 }
272
273 pub fn are_foreign_keys_enabled(&self) -> QueryResult<bool> {
277 self.raw_connection
278 .get_db_config_bool(ffi::SQLITE_DBCONFIG_ENABLE_FKEY)
279 }
280
281 pub fn set_double_quoted_strings_dml(&mut self, enabled: bool) -> QueryResult<()> {
289 self.raw_connection
290 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML, enabled)
291 }
292
293 pub fn are_double_quoted_strings_dml_enabled(&self) -> QueryResult<bool> {
297 self.raw_connection
298 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DML)
299 }
300
301 pub fn set_double_quoted_strings_ddl(&mut self, enabled: bool) -> QueryResult<()> {
309 self.raw_connection
310 .set_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL, enabled)
311 }
312
313 pub fn are_double_quoted_strings_ddl_enabled(&self) -> QueryResult<bool> {
317 self.raw_connection
318 .get_db_config_bool(ffi::SQLITE_DBCONFIG_DQS_DDL)
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325 use crate::connection::DatabaseVersion;
326 use crate::dsl::sql;
327 use crate::prelude::*;
328 #[cfg(not(miri))]
329 use crate::sql_types::Text;
330
331 fn connection() -> SqliteConnection {
332 SqliteConnection::establish(":memory:").unwrap()
333 }
334
335 #[diesel_test_helper::test]
338 fn db_config_defensive_roundtrip() {
339 let conn = &mut connection();
340 conn.set_defensive(true).unwrap();
341 assert!(conn.is_defensive().unwrap());
342 conn.set_defensive(false).unwrap();
343 assert!(!conn.is_defensive().unwrap());
344 }
345
346 #[diesel_test_helper::test]
347 fn db_config_trusted_schema_roundtrip() {
348 let conn = &mut connection();
349 conn.set_trusted_schema(false).unwrap();
350 assert!(!conn.is_trusted_schema().unwrap());
351 conn.set_trusted_schema(true).unwrap();
352 assert!(conn.is_trusted_schema().unwrap());
353 }
354
355 #[diesel_test_helper::test]
356 fn db_config_with_load_extension_enabled_scopes_the_flag() {
357 let conn = &mut connection();
358 conn.with_load_extension_enabled(|conn| {
359 assert!(conn.is_load_extension_enabled().unwrap());
361 QueryResult::Ok(())
362 })
363 .unwrap();
364 assert!(!conn.is_load_extension_enabled().unwrap());
366 }
367
368 #[cfg(all(
369 feature = "std",
370 not(all(target_family = "wasm", target_os = "unknown"))
371 ))]
372 #[diesel_test_helper::test]
373 fn with_load_extension_enabled_disables_after_panic() {
374 let conn = &mut connection();
375 let outcome = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
376 conn.with_load_extension_enabled(|_conn| -> QueryResult<()> {
377 panic!("boom inside closure");
378 })
379 }));
380 assert!(outcome.is_err(), "panic should propagate");
381 assert!(
382 !conn.is_load_extension_enabled().unwrap(),
383 "extension loading must be disabled again after a panic"
384 );
385 }
386
387 #[diesel_test_helper::test]
388 fn db_config_triggers_roundtrip() {
389 let conn = &mut connection();
390 conn.set_triggers_enabled(false).unwrap();
391 assert!(!conn.are_triggers_enabled().unwrap());
392 conn.set_triggers_enabled(true).unwrap();
393 assert!(conn.are_triggers_enabled().unwrap());
394 }
395
396 #[diesel_test_helper::test]
397 fn db_config_views_roundtrip() {
398 let conn = &mut connection();
399 conn.set_views_enabled(false).unwrap();
400 assert!(!conn.are_views_enabled().unwrap());
401 conn.set_views_enabled(true).unwrap();
402 assert!(conn.are_views_enabled().unwrap());
403 }
404
405 #[diesel_test_helper::test]
406 fn db_config_foreign_keys_roundtrip() {
407 let conn = &mut connection();
408 conn.set_foreign_keys_enabled(true).unwrap();
409 assert!(conn.are_foreign_keys_enabled().unwrap());
410 conn.set_foreign_keys_enabled(false).unwrap();
411 assert!(!conn.are_foreign_keys_enabled().unwrap());
412 }
413
414 #[diesel_test_helper::test]
415 fn db_config_dqs_dml_roundtrip() {
416 let conn = &mut connection();
417 conn.set_double_quoted_strings_dml(false).unwrap();
418 assert!(!conn.are_double_quoted_strings_dml_enabled().unwrap());
419 conn.set_double_quoted_strings_dml(true).unwrap();
420 assert!(conn.are_double_quoted_strings_dml_enabled().unwrap());
421 }
422
423 #[diesel_test_helper::test]
424 fn db_config_dqs_ddl_roundtrip() {
425 let conn = &mut connection();
426 conn.set_double_quoted_strings_ddl(false).unwrap();
427 assert!(!conn.are_double_quoted_strings_ddl_enabled().unwrap());
428 conn.set_double_quoted_strings_ddl(true).unwrap();
429 assert!(conn.are_double_quoted_strings_ddl_enabled().unwrap());
430 }
431
432 #[diesel_test_helper::test]
433 fn db_config_fts3_tokenizer_roundtrip() {
434 let conn = &mut connection();
435 conn.set_fts3_tokenizer_enabled(false).unwrap();
436 assert!(!conn.is_fts3_tokenizer_enabled().unwrap());
437 conn.set_fts3_tokenizer_enabled(true).unwrap();
438 assert!(conn.is_fts3_tokenizer_enabled().unwrap());
439 }
440
441 #[diesel_test_helper::test]
442 fn db_config_writable_schema_roundtrip() {
443 let conn = &mut connection();
444 conn.set_writable_schema(false).unwrap();
445 assert!(!conn.is_writable_schema().unwrap());
446 conn.set_writable_schema(true).unwrap();
447 assert!(conn.is_writable_schema().unwrap());
448 }
449
450 #[diesel_test_helper::test]
451 fn db_config_attach_create_roundtrip() {
452 let conn = &mut connection();
453 if conn.version() < DatabaseVersion::new(3, 49, 0) {
454 return;
455 }
456 conn.set_attach_create_enabled(false).unwrap();
457 assert!(!conn.is_attach_create_enabled().unwrap());
458 conn.set_attach_create_enabled(true).unwrap();
459 assert!(conn.is_attach_create_enabled().unwrap());
460 }
461
462 #[diesel_test_helper::test]
463 fn db_config_attach_write_roundtrip() {
464 let conn = &mut connection();
465 if conn.version() < DatabaseVersion::new(3, 49, 0) {
466 return;
467 }
468 conn.set_attach_write_enabled(false).unwrap();
469 assert!(!conn.is_attach_write_enabled().unwrap());
470 conn.set_attach_write_enabled(true).unwrap();
471 assert!(conn.is_attach_write_enabled().unwrap());
472 }
473
474 #[diesel_test_helper::test]
477 fn defensive_mode_blocks_writable_schema() {
478 let conn = &mut connection();
479 conn.set_defensive(true).unwrap();
480 let _ = crate::sql_query("PRAGMA writable_schema = ON").execute(conn);
482 assert!(!conn.is_writable_schema().unwrap());
483 }
484
485 #[diesel_test_helper::test]
486 fn foreign_keys_enabled_enforces_constraints() {
487 let conn = &mut connection();
488 conn.set_foreign_keys_enabled(true).unwrap();
489
490 crate::sql_query("CREATE TABLE parent (id INTEGER PRIMARY KEY)")
491 .execute(conn)
492 .unwrap();
493 crate::sql_query(
494 "CREATE TABLE child (id INTEGER PRIMARY KEY, parent_id INTEGER REFERENCES parent(id))",
495 )
496 .execute(conn)
497 .unwrap();
498
499 if cfg!(not(miri)) {
500 let result =
503 crate::sql_query("INSERT INTO child (id, parent_id) VALUES (1, 999)").execute(conn);
504 assert!(result.is_err());
505 }
506 }
507
508 #[diesel_test_helper::test]
509 fn views_disabled_blocks_view_queries() {
510 let conn = &mut connection();
511 crate::sql_query("CREATE TABLE base (id INTEGER PRIMARY KEY)")
512 .execute(conn)
513 .unwrap();
514 crate::sql_query("INSERT INTO base (id) VALUES (1)")
515 .execute(conn)
516 .unwrap();
517 crate::sql_query("CREATE VIEW base_view AS SELECT id FROM base")
518 .execute(conn)
519 .unwrap();
520
521 conn.set_views_enabled(true).unwrap();
523 assert!(
524 crate::sql_query("SELECT id FROM base_view")
525 .execute(conn)
526 .is_ok()
527 );
528
529 if cfg!(not(miri)) {
530 conn.set_views_enabled(false).unwrap();
533 assert!(
534 crate::sql_query("SELECT id FROM base_view")
535 .execute(conn)
536 .is_err()
537 );
538 }
539 }
540
541 #[diesel_test_helper::test]
542 fn triggers_disabled_prevents_firing() {
543 let conn = &mut connection();
544 crate::sql_query("CREATE TABLE source (id INTEGER PRIMARY KEY)")
545 .execute(conn)
546 .unwrap();
547 crate::sql_query("CREATE TABLE trigger_log (n INTEGER)")
548 .execute(conn)
549 .unwrap();
550 crate::sql_query("CREATE TRIGGER log_insert AFTER INSERT ON source BEGIN INSERT INTO trigger_log (n) VALUES (1); END")
551 .execute(conn)
552 .unwrap();
553
554 conn.set_triggers_enabled(false).unwrap();
556 crate::sql_query("INSERT INTO source (id) VALUES (1)")
557 .execute(conn)
558 .unwrap();
559 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
560 .get_result(conn)
561 .unwrap();
562 assert_eq!(0, count, "trigger should not fire while disabled");
563
564 conn.set_triggers_enabled(true).unwrap();
566 crate::sql_query("INSERT INTO source (id) VALUES (2)")
567 .execute(conn)
568 .unwrap();
569 let count: i64 = sql::<crate::sql_types::BigInt>("SELECT COUNT(*) FROM trigger_log")
570 .get_result(conn)
571 .unwrap();
572 assert_eq!(1, count, "trigger should fire while enabled");
573 }
574
575 #[cfg(not(miri))] #[diesel_test_helper::test]
577 fn dqs_dml_controls_double_quoted_string_literals() {
578 let conn = &mut connection();
579
580 conn.set_double_quoted_strings_dml(false).unwrap();
583
584 let disabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
585 assert!(disabled.is_err());
586
587 conn.set_double_quoted_strings_dml(true).unwrap();
589 let enabled = sql::<Text>(r#"SELECT "bare_token""#).get_result::<String>(conn);
590 assert_eq!(Ok("bare_token".to_owned()), enabled);
591 }
592
593 #[diesel_test_helper::test]
594 fn dqs_ddl_controls_double_quoted_string_literals() {
595 let conn = &mut connection();
596
597 conn.set_double_quoted_strings_ddl(false).unwrap();
600 if cfg!(not(miri)) {
602 let disabled = crate::sql_query(
603 r#"CREATE TABLE dqs_off (name TEXT, CHECK (name <> "not_a_column"))"#,
604 )
605 .execute(conn);
606 assert!(disabled.is_err());
607 }
608
609 conn.set_double_quoted_strings_ddl(true).unwrap();
612 let enabled =
613 crate::sql_query(r#"CREATE TABLE dqs_on (name TEXT, CHECK (name <> "not_a_column"))"#)
614 .execute(conn);
615 assert!(enabled.is_ok());
616 }
617
618 #[diesel_test_helper::test]
619 fn writable_schema_controls_direct_sqlite_master_writes() {
620 let conn = &mut connection();
621 crate::sql_query("CREATE TABLE protected (id INTEGER PRIMARY KEY)")
622 .execute(conn)
623 .unwrap();
624
625 let update =
626 "UPDATE sqlite_master SET sql = sql WHERE type = 'table' AND name = 'protected'";
627
628 if cfg!(not(miri)) {
629 conn.set_writable_schema(false).unwrap();
632 assert!(crate::sql_query(update).execute(conn).is_err());
633 }
634
635 conn.set_writable_schema(true).unwrap();
637 assert!(crate::sql_query(update).execute(conn).is_ok());
638 }
639
640 #[cfg(not(miri))] #[diesel_test_helper::test]
642 fn fts3_tokenizer_disabled_blocks_the_function() {
643 let conn = &mut connection();
644
645 conn.set_fts3_tokenizer_enabled(true).unwrap();
647 let enabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
648 .get_result::<Vec<u8>>(conn);
649 if enabled.is_err() {
650 return;
652 }
653
654 conn.set_fts3_tokenizer_enabled(false).unwrap();
656 let disabled = sql::<crate::sql_types::Binary>("SELECT fts3_tokenizer('simple')")
657 .get_result::<Vec<u8>>(conn);
658 assert!(disabled.is_err());
659 }
660
661 #[cfg(not(any(all(target_family = "wasm", target_os = "unknown"), miri)))]
664 fn temp_db_path(name: &str) -> (tempfile::TempDir, std::path::PathBuf) {
665 let dir = tempfile::tempdir().unwrap();
666 let path = dir.path().join(name);
667 (dir, path)
668 }
669
670 #[cfg(not(any(all(target_family = "wasm", target_os = "unknown"), miri)))]
671 #[diesel_test_helper::test]
672 fn attach_create_disabled_blocks_new_database_files() {
673 let conn = &mut connection();
674
675 if conn.set_attach_create_enabled(false).is_err() {
678 return;
679 }
680
681 let (_dir, path) = temp_db_path("create.db");
682
683 assert!(
685 conn.attach_database(path.to_str().unwrap(), "aux_create")
686 .is_err()
687 );
688
689 conn.set_attach_create_enabled(true).unwrap();
691 conn.attach_database(path.to_str().unwrap(), "aux_create")
692 .unwrap();
693 conn.detach_database("aux_create").unwrap();
694 }
695
696 #[cfg(not(any(all(target_family = "wasm", target_os = "unknown"), miri)))]
697 #[diesel_test_helper::test]
698 fn attach_write_disabled_opens_attached_databases_read_only() {
699 let conn = &mut connection();
700
701 if conn.set_attach_write_enabled(false).is_err() {
705 return;
706 }
707
708 let (_dir, path) = temp_db_path("write.db");
710 {
711 let mut seed = SqliteConnection::establish(path.to_str().unwrap()).unwrap();
712 crate::sql_query("CREATE TABLE t (id INTEGER)")
713 .execute(&mut seed)
714 .unwrap();
715 }
716
717 conn.attach_database(path.to_str().unwrap(), "aux_write")
719 .unwrap();
720 assert!(
721 crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
722 .execute(conn)
723 .is_err()
724 );
725 conn.detach_database("aux_write").unwrap();
726
727 conn.set_attach_write_enabled(true).unwrap();
729 conn.attach_database(path.to_str().unwrap(), "aux_write")
730 .unwrap();
731 crate::sql_query("INSERT INTO aux_write.t (id) VALUES (1)")
732 .execute(conn)
733 .unwrap();
734 conn.detach_database("aux_write").unwrap();
735 }
736}