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::export as ffi;
6
7mod bind_collector;
8mod functions;
9mod owned_row;
10mod raw;
11mod row;
12mod serialized_database;
13mod sqlite_value;
14mod statement_iterator;
15mod stmt;
16
17pub(in crate::sqlite) use self::bind_collector::SqliteBindCollector;
18pub use self::bind_collector::SqliteBindValue;
19pub use self::serialized_database::SerializedDatabase;
20pub use self::sqlite_value::SqliteValue;
21
22use std::os::raw as libc;
23
24use self::raw::RawConnection;
25use self::statement_iterator::*;
26use self::stmt::{Statement, StatementUse};
27use super::SqliteAggregateFunction;
28use crate::connection::instrumentation::{DynInstrumentation, StrQueryHelper};
29use crate::connection::statement_cache::StatementCache;
30use crate::connection::*;
31use crate::deserialize::{FromSqlRow, StaticallySizedRow};
32use crate::expression::QueryMetadata;
33use crate::query_builder::*;
34use crate::result::*;
35use crate::serialize::ToSql;
36use crate::sql_types::{HasSqlType, TypeMetadata};
37use crate::sqlite::Sqlite;
38
39#[allow(missing_debug_implementations)]
123#[cfg(feature = "sqlite")]
124pub struct SqliteConnection {
125 statement_cache: StatementCache<Sqlite, Statement>,
129 raw_connection: RawConnection,
130 transaction_state: AnsiTransactionManager,
131 metadata_lookup: (),
134 instrumentation: DynInstrumentation,
135}
136
137#[allow(unsafe_code)]
141unsafe impl Send for SqliteConnection {}
142
143impl SimpleConnection for SqliteConnection {
144 fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
145 self.instrumentation
146 .on_connection_event(InstrumentationEvent::StartQuery {
147 query: &StrQueryHelper::new(query),
148 });
149 let resp = self.raw_connection.exec(query);
150 self.instrumentation
151 .on_connection_event(InstrumentationEvent::FinishQuery {
152 query: &StrQueryHelper::new(query),
153 error: resp.as_ref().err(),
154 });
155 resp
156 }
157}
158
159impl ConnectionSealed for SqliteConnection {}
160
161impl Connection for SqliteConnection {
162 type Backend = Sqlite;
163 type TransactionManager = AnsiTransactionManager;
164
165 fn establish(database_url: &str) -> ConnectionResult<Self> {
181 let mut instrumentation = DynInstrumentation::default_instrumentation();
182 instrumentation.on_connection_event(InstrumentationEvent::StartEstablishConnection {
183 url: database_url,
184 });
185
186 let establish_result = Self::establish_inner(database_url);
187 instrumentation.on_connection_event(InstrumentationEvent::FinishEstablishConnection {
188 url: database_url,
189 error: establish_result.as_ref().err(),
190 });
191 let mut conn = establish_result?;
192 conn.instrumentation = instrumentation;
193 Ok(conn)
194 }
195
196 fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
197 where
198 T: QueryFragment<Self::Backend> + QueryId,
199 {
200 let statement_use = self.prepared_query(source)?;
201 statement_use.run().and_then(|_| {
202 self.raw_connection
203 .rows_affected_by_last_query()
204 .map_err(Error::DeserializationError)
205 })
206 }
207
208 fn transaction_state(&mut self) -> &mut AnsiTransactionManager
209 where
210 Self: Sized,
211 {
212 &mut self.transaction_state
213 }
214
215 fn instrumentation(&mut self) -> &mut dyn Instrumentation {
216 &mut *self.instrumentation
217 }
218
219 fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
220 self.instrumentation = instrumentation.into();
221 }
222
223 fn set_prepared_statement_cache_size(&mut self, size: CacheSize) {
224 self.statement_cache.set_cache_size(size);
225 }
226}
227
228impl LoadConnection<DefaultLoadingMode> for SqliteConnection {
229 type Cursor<'conn, 'query> = StatementIterator<'conn, 'query>;
230 type Row<'conn, 'query> = self::row::SqliteRow<'conn, 'query>;
231
232 fn load<'conn, 'query, T>(
233 &'conn mut self,
234 source: T,
235 ) -> QueryResult<Self::Cursor<'conn, 'query>>
236 where
237 T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
238 Self::Backend: QueryMetadata<T::SqlType>,
239 {
240 let statement = self.prepared_query(source)?;
241
242 Ok(StatementIterator::new(statement))
243 }
244}
245
246impl WithMetadataLookup for SqliteConnection {
247 fn metadata_lookup(&mut self) -> &mut <Sqlite as TypeMetadata>::MetadataLookup {
248 &mut self.metadata_lookup
249 }
250}
251
252#[cfg(feature = "r2d2")]
253impl crate::r2d2::R2D2Connection for crate::sqlite::SqliteConnection {
254 fn ping(&mut self) -> QueryResult<()> {
255 use crate::RunQueryDsl;
256
257 crate::r2d2::CheckConnectionQuery.execute(self).map(|_| ())
258 }
259
260 fn is_broken(&mut self) -> bool {
261 AnsiTransactionManager::is_broken_transaction_manager(self)
262 }
263}
264
265impl MultiConnectionHelper for SqliteConnection {
266 fn to_any<'a>(
267 lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
268 ) -> &mut (dyn std::any::Any + 'a) {
269 lookup
270 }
271
272 fn from_any(
273 lookup: &mut dyn std::any::Any,
274 ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup> {
275 lookup.downcast_mut()
276 }
277}
278
279impl SqliteConnection {
280 pub fn immediate_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
302 where
303 F: FnOnce(&mut Self) -> Result<T, E>,
304 E: From<Error>,
305 {
306 self.transaction_sql(f, "BEGIN IMMEDIATE")
307 }
308
309 pub fn exclusive_transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
331 where
332 F: FnOnce(&mut Self) -> Result<T, E>,
333 E: From<Error>,
334 {
335 self.transaction_sql(f, "BEGIN EXCLUSIVE")
336 }
337
338 fn transaction_sql<T, E, F>(&mut self, f: F, sql: &str) -> Result<T, E>
339 where
340 F: FnOnce(&mut Self) -> Result<T, E>,
341 E: From<Error>,
342 {
343 AnsiTransactionManager::begin_transaction_sql(&mut *self, sql)?;
344 match f(&mut *self) {
345 Ok(value) => {
346 AnsiTransactionManager::commit_transaction(&mut *self)?;
347 Ok(value)
348 }
349 Err(e) => {
350 AnsiTransactionManager::rollback_transaction(&mut *self)?;
351 Err(e)
352 }
353 }
354 }
355
356 fn prepared_query<'conn, 'query, T>(
357 &'conn mut self,
358 source: T,
359 ) -> QueryResult<StatementUse<'conn, 'query>>
360 where
361 T: QueryFragment<Sqlite> + QueryId + 'query,
362 {
363 self.instrumentation
364 .on_connection_event(InstrumentationEvent::StartQuery {
365 query: &crate::debug_query(&source),
366 });
367 let raw_connection = &self.raw_connection;
368 let cache = &mut self.statement_cache;
369 let statement = match cache.cached_statement(
370 &source,
371 &Sqlite,
372 &[],
373 raw_connection,
374 Statement::prepare,
375 &mut *self.instrumentation,
376 ) {
377 Ok(statement) => statement,
378 Err(e) => {
379 self.instrumentation
380 .on_connection_event(InstrumentationEvent::FinishQuery {
381 query: &crate::debug_query(&source),
382 error: Some(&e),
383 });
384
385 return Err(e);
386 }
387 };
388
389 StatementUse::bind(statement, source, &mut *self.instrumentation)
390 }
391
392 #[doc(hidden)]
393 pub fn register_sql_function<ArgsSqlType, RetSqlType, Args, Ret, F>(
394 &mut self,
395 fn_name: &str,
396 deterministic: bool,
397 mut f: F,
398 ) -> QueryResult<()>
399 where
400 F: FnMut(Args) -> Ret + std::panic::UnwindSafe + Send + 'static,
401 Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
402 Ret: ToSql<RetSqlType, Sqlite>,
403 Sqlite: HasSqlType<RetSqlType>,
404 {
405 functions::register(
406 &self.raw_connection,
407 fn_name,
408 deterministic,
409 move |_, args| f(args),
410 )
411 }
412
413 #[doc(hidden)]
414 pub fn register_noarg_sql_function<RetSqlType, Ret, F>(
415 &self,
416 fn_name: &str,
417 deterministic: bool,
418 f: F,
419 ) -> QueryResult<()>
420 where
421 F: FnMut() -> Ret + std::panic::UnwindSafe + Send + 'static,
422 Ret: ToSql<RetSqlType, Sqlite>,
423 Sqlite: HasSqlType<RetSqlType>,
424 {
425 functions::register_noargs(&self.raw_connection, fn_name, deterministic, f)
426 }
427
428 #[doc(hidden)]
429 pub fn register_aggregate_function<ArgsSqlType, RetSqlType, Args, Ret, A>(
430 &mut self,
431 fn_name: &str,
432 ) -> QueryResult<()>
433 where
434 A: SqliteAggregateFunction<Args, Output = Ret> + 'static + Send + std::panic::UnwindSafe,
435 Args: FromSqlRow<ArgsSqlType, Sqlite> + StaticallySizedRow<ArgsSqlType, Sqlite>,
436 Ret: ToSql<RetSqlType, Sqlite>,
437 Sqlite: HasSqlType<RetSqlType>,
438 {
439 functions::register_aggregate::<_, _, _, _, A>(&self.raw_connection, fn_name)
440 }
441
442 pub fn register_collation<F>(&mut self, collation_name: &str, collation: F) -> QueryResult<()>
478 where
479 F: Fn(&str, &str) -> std::cmp::Ordering + Send + 'static + std::panic::UnwindSafe,
480 {
481 self.raw_connection
482 .register_collation_function(collation_name, collation)
483 }
484
485 pub fn serialize_database_to_buffer(&mut self) -> SerializedDatabase {
494 self.raw_connection.serialize()
495 }
496
497 pub fn deserialize_readonly_database_from_buffer(&mut self, data: &[u8]) -> QueryResult<()> {
534 self.raw_connection.deserialize(data)
535 }
536
537 fn register_diesel_sql_functions(&self) -> QueryResult<()> {
538 use crate::sql_types::{Integer, Text};
539
540 functions::register::<Text, Integer, _, _, _>(
541 &self.raw_connection,
542 "diesel_manage_updated_at",
543 false,
544 |conn, table_name: String| {
545 conn.exec(&format!(
546 include_str!("diesel_manage_updated_at.sql"),
547 table_name = table_name
548 ))
549 .expect("Failed to create trigger");
550 0 },
552 )
553 }
554
555 fn establish_inner(database_url: &str) -> Result<SqliteConnection, ConnectionError> {
556 use crate::result::ConnectionError::CouldntSetupConfiguration;
557 let raw_connection = RawConnection::establish(database_url)?;
558 let conn = Self {
559 statement_cache: StatementCache::new(),
560 raw_connection,
561 transaction_state: AnsiTransactionManager::default(),
562 metadata_lookup: (),
563 instrumentation: DynInstrumentation::none(),
564 };
565 conn.register_diesel_sql_functions()
566 .map_err(CouldntSetupConfiguration)?;
567 Ok(conn)
568 }
569}
570
571fn error_message(err_code: libc::c_int) -> &'static str {
572 ffi::code_to_str(err_code)
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578 use crate::dsl::sql;
579 use crate::prelude::*;
580 use crate::sql_types::{Integer, Text};
581
582 fn connection() -> SqliteConnection {
583 SqliteConnection::establish(":memory:").unwrap()
584 }
585
586 #[declare_sql_function]
587 extern "SQL" {
588 fn fun_case(x: Text) -> Text;
589 fn my_add(x: Integer, y: Integer) -> Integer;
590 fn answer() -> Integer;
591 fn add_counter(x: Integer) -> Integer;
592
593 #[aggregate]
594 fn my_sum(expr: Integer) -> Integer;
595 #[aggregate]
596 fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable<Integer>;
597 }
598
599 #[diesel_test_helper::test]
600 fn database_serializes_and_deserializes_successfully() {
601 let expected_users = vec![
602 (
603 1,
604 "John Doe".to_string(),
605 "john.doe@example.com".to_string(),
606 ),
607 (
608 2,
609 "Jane Doe".to_string(),
610 "jane.doe@example.com".to_string(),
611 ),
612 ];
613
614 let conn1 = &mut connection();
615 let _ =
616 crate::sql_query("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
617 .execute(conn1);
618 let _ = crate::sql_query("INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com'), ('Jane Doe', 'jane.doe@example.com')")
619 .execute(conn1);
620
621 let serialized_database = conn1.serialize_database_to_buffer();
622
623 let conn2 = &mut connection();
624 conn2
625 .deserialize_readonly_database_from_buffer(serialized_database.as_slice())
626 .unwrap();
627
628 let query = sql::<(Integer, Text, Text)>("SELECT id, name, email FROM users ORDER BY id");
629 let actual_users = query.load::<(i32, String, String)>(conn2).unwrap();
630
631 assert_eq!(expected_users, actual_users);
632 }
633
634 #[diesel_test_helper::test]
635 fn register_custom_function() {
636 let connection = &mut connection();
637 fun_case_utils::register_impl(connection, |x: String| {
638 x.chars()
639 .enumerate()
640 .map(|(i, c)| {
641 if i % 2 == 0 {
642 c.to_lowercase().to_string()
643 } else {
644 c.to_uppercase().to_string()
645 }
646 })
647 .collect::<String>()
648 })
649 .unwrap();
650
651 let mapped_string = crate::select(fun_case("foobar"))
652 .get_result::<String>(connection)
653 .unwrap();
654 assert_eq!("fOoBaR", mapped_string);
655 }
656
657 #[diesel_test_helper::test]
658 fn register_multiarg_function() {
659 let connection = &mut connection();
660 my_add_utils::register_impl(connection, |x: i32, y: i32| x + y).unwrap();
661
662 let added = crate::select(my_add(1, 2)).get_result::<i32>(connection);
663 assert_eq!(Ok(3), added);
664 }
665
666 #[diesel_test_helper::test]
667 fn register_noarg_function() {
668 let connection = &mut connection();
669 answer_utils::register_impl(connection, || 42).unwrap();
670
671 let answer = crate::select(answer()).get_result::<i32>(connection);
672 assert_eq!(Ok(42), answer);
673 }
674
675 #[diesel_test_helper::test]
676 fn register_nondeterministic_noarg_function() {
677 let connection = &mut connection();
678 answer_utils::register_nondeterministic_impl(connection, || 42).unwrap();
679
680 let answer = crate::select(answer()).get_result::<i32>(connection);
681 assert_eq!(Ok(42), answer);
682 }
683
684 #[diesel_test_helper::test]
685 fn register_nondeterministic_function() {
686 let connection = &mut connection();
687 let mut y = 0;
688 add_counter_utils::register_nondeterministic_impl(connection, move |x: i32| {
689 y += 1;
690 x + y
691 })
692 .unwrap();
693
694 let added = crate::select((add_counter(1), add_counter(1), add_counter(1)))
695 .get_result::<(i32, i32, i32)>(connection);
696 assert_eq!(Ok((2, 3, 4)), added);
697 }
698
699 #[derive(Default)]
700 struct MySum {
701 sum: i32,
702 }
703
704 impl SqliteAggregateFunction<i32> for MySum {
705 type Output = i32;
706
707 fn step(&mut self, expr: i32) {
708 self.sum += expr;
709 }
710
711 fn finalize(aggregator: Option<Self>) -> Self::Output {
712 aggregator.map(|a| a.sum).unwrap_or_default()
713 }
714 }
715
716 table! {
717 my_sum_example {
718 id -> Integer,
719 value -> Integer,
720 }
721 }
722
723 #[diesel_test_helper::test]
724 fn register_aggregate_function() {
725 use self::my_sum_example::dsl::*;
726
727 let connection = &mut connection();
728 crate::sql_query(
729 "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
730 )
731 .execute(connection)
732 .unwrap();
733 crate::sql_query("INSERT INTO my_sum_example (value) VALUES (1), (2), (3)")
734 .execute(connection)
735 .unwrap();
736
737 my_sum_utils::register_impl::<MySum, _>(connection).unwrap();
738
739 let result = my_sum_example
740 .select(my_sum(value))
741 .get_result::<i32>(connection);
742 assert_eq!(Ok(6), result);
743 }
744
745 #[diesel_test_helper::test]
746 fn register_aggregate_function_returns_finalize_default_on_empty_set() {
747 use self::my_sum_example::dsl::*;
748
749 let connection = &mut connection();
750 crate::sql_query(
751 "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)",
752 )
753 .execute(connection)
754 .unwrap();
755
756 my_sum_utils::register_impl::<MySum, _>(connection).unwrap();
757
758 let result = my_sum_example
759 .select(my_sum(value))
760 .get_result::<i32>(connection);
761 assert_eq!(Ok(0), result);
762 }
763
764 #[derive(Default)]
765 struct RangeMax<T> {
766 max_value: Option<T>,
767 }
768
769 impl<T: Default + Ord + Copy + Clone> SqliteAggregateFunction<(T, T, T)> for RangeMax<T> {
770 type Output = Option<T>;
771
772 fn step(&mut self, (x0, x1, x2): (T, T, T)) {
773 let max = if x0 >= x1 && x0 >= x2 {
774 x0
775 } else if x1 >= x0 && x1 >= x2 {
776 x1
777 } else {
778 x2
779 };
780
781 self.max_value = match self.max_value {
782 Some(current_max_value) if max > current_max_value => Some(max),
783 None => Some(max),
784 _ => self.max_value,
785 };
786 }
787
788 fn finalize(aggregator: Option<Self>) -> Self::Output {
789 aggregator?.max_value
790 }
791 }
792
793 table! {
794 range_max_example {
795 id -> Integer,
796 value1 -> Integer,
797 value2 -> Integer,
798 value3 -> Integer,
799 }
800 }
801
802 #[diesel_test_helper::test]
803 fn register_aggregate_multiarg_function() {
804 use self::range_max_example::dsl::*;
805
806 let connection = &mut connection();
807 crate::sql_query(
808 r#"CREATE TABLE range_max_example (
809 id integer primary key autoincrement,
810 value1 integer,
811 value2 integer,
812 value3 integer
813 )"#,
814 )
815 .execute(connection)
816 .unwrap();
817 crate::sql_query(
818 "INSERT INTO range_max_example (value1, value2, value3) VALUES (3, 2, 1), (2, 2, 2)",
819 )
820 .execute(connection)
821 .unwrap();
822
823 range_max_utils::register_impl::<RangeMax<i32>, _, _, _>(connection).unwrap();
824 let result = range_max_example
825 .select(range_max(value1, value2, value3))
826 .get_result::<Option<i32>>(connection)
827 .unwrap();
828 assert_eq!(Some(3), result);
829 }
830
831 table! {
832 my_collation_example {
833 id -> Integer,
834 value -> Text,
835 }
836 }
837
838 #[diesel_test_helper::test]
839 fn register_collation_function() {
840 use self::my_collation_example::dsl::*;
841
842 let connection = &mut connection();
843
844 connection
845 .register_collation("RUSTNOCASE", |rhs, lhs| {
846 rhs.to_lowercase().cmp(&lhs.to_lowercase())
847 })
848 .unwrap();
849
850 crate::sql_query(
851 "CREATE TABLE my_collation_example (id integer primary key autoincrement, value text collate RUSTNOCASE)",
852 ).execute(connection)
853 .unwrap();
854 crate::sql_query(
855 "INSERT INTO my_collation_example (value) VALUES ('foo'), ('FOo'), ('f00')",
856 )
857 .execute(connection)
858 .unwrap();
859
860 let result = my_collation_example
861 .filter(value.eq("foo"))
862 .select(value)
863 .load::<String>(connection);
864 assert_eq!(
865 Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
866 result.as_ref().map(|vec| vec.as_ref())
867 );
868
869 let result = my_collation_example
870 .filter(value.eq("FOO"))
871 .select(value)
872 .load::<String>(connection);
873 assert_eq!(
874 Ok(&["foo".to_owned(), "FOo".to_owned()][..]),
875 result.as_ref().map(|vec| vec.as_ref())
876 );
877
878 let result = my_collation_example
879 .filter(value.eq("f00"))
880 .select(value)
881 .load::<String>(connection);
882 assert_eq!(
883 Ok(&["f00".to_owned()][..]),
884 result.as_ref().map(|vec| vec.as_ref())
885 );
886
887 let result = my_collation_example
888 .filter(value.eq("F00"))
889 .select(value)
890 .load::<String>(connection);
891 assert_eq!(
892 Ok(&["f00".to_owned()][..]),
893 result.as_ref().map(|vec| vec.as_ref())
894 );
895
896 let result = my_collation_example
897 .filter(value.eq("oof"))
898 .select(value)
899 .load::<String>(connection);
900 assert_eq!(Ok(&[][..]), result.as_ref().map(|vec| vec.as_ref()));
901 }
902
903 #[diesel_test_helper::test]
905 fn test_correct_seralization_of_owned_strings() {
906 use crate::prelude::*;
907
908 #[derive(Debug, crate::expression::AsExpression)]
909 #[diesel(sql_type = diesel::sql_types::Text)]
910 struct CustomWrapper(String);
911
912 impl crate::serialize::ToSql<Text, Sqlite> for CustomWrapper {
913 fn to_sql<'b>(
914 &'b self,
915 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
916 ) -> crate::serialize::Result {
917 out.set_value(self.0.to_string());
918 Ok(crate::serialize::IsNull::No)
919 }
920 }
921
922 let connection = &mut connection();
923
924 let res = crate::select(
925 CustomWrapper("".into())
926 .into_sql::<crate::sql_types::Text>()
927 .nullable(),
928 )
929 .get_result::<Option<String>>(connection)
930 .unwrap();
931 assert_eq!(res, Some(String::new()));
932 }
933
934 #[diesel_test_helper::test]
935 fn test_correct_seralization_of_owned_bytes() {
936 use crate::prelude::*;
937
938 #[derive(Debug, crate::expression::AsExpression)]
939 #[diesel(sql_type = diesel::sql_types::Binary)]
940 struct CustomWrapper(Vec<u8>);
941
942 impl crate::serialize::ToSql<crate::sql_types::Binary, Sqlite> for CustomWrapper {
943 fn to_sql<'b>(
944 &'b self,
945 out: &mut crate::serialize::Output<'b, '_, Sqlite>,
946 ) -> crate::serialize::Result {
947 out.set_value(self.0.clone());
948 Ok(crate::serialize::IsNull::No)
949 }
950 }
951
952 let connection = &mut connection();
953
954 let res = crate::select(
955 CustomWrapper(Vec::new())
956 .into_sql::<crate::sql_types::Binary>()
957 .nullable(),
958 )
959 .get_result::<Option<Vec<u8>>>(connection)
960 .unwrap();
961 assert_eq!(res, Some(Vec::new()));
962 }
963
964 #[diesel_test_helper::test]
965 fn correctly_handle_empty_query() {
966 let check_empty_query_error = |r: crate::QueryResult<usize>| {
967 assert!(r.is_err());
968 let err = r.unwrap_err();
969 assert!(
970 matches!(err, crate::result::Error::QueryBuilderError(ref b) if b.is::<crate::result::EmptyQuery>()),
971 "Expected a query builder error, but got {err}"
972 );
973 };
974 let connection = &mut SqliteConnection::establish(":memory:").unwrap();
975 check_empty_query_error(crate::sql_query("").execute(connection));
976 check_empty_query_error(crate::sql_query(" ").execute(connection));
977 check_empty_query_error(crate::sql_query("\n\t").execute(connection));
978 check_empty_query_error(crate::sql_query("-- SELECT 1;").execute(connection));
979 }
980
981 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
982 #[wasm_bindgen_test::wasm_bindgen_test]
983 fn test_sqlite_wasm_vfs_default() {
984 SqliteConnection::establish("test_sqlite_wasm_vfs_default.db").unwrap();
985 }
986
987 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
988 #[wasm_bindgen_test::wasm_bindgen_test]
989 async fn test_sqlite_wasm_vfs_opfs_sahpool() {
990 let util = sqlite_wasm_rs::export::install_opfs_sahpool(None, false)
991 .await
992 .unwrap();
993 SqliteConnection::establish("file:test_sqlite_wasm_vfs_opfs_sahpool.db?vfs=opfs-sahpool")
994 .unwrap();
995 assert!(util.get_file_count() > 0);
996 }
997}