@@ -37,14 +37,6 @@ fn escape_sqlite_string_literal(text: &str) -> String {
3737 text. replace ( '\'' , "''" )
3838}
3939
40- fn vacuum_into_literal ( conn : & Arc < Connection > , dest_literal : & str ) -> anyhow:: Result < ( ) > {
41- conn. execute ( format ! (
42- "VACUUM INTO '{}'" ,
43- escape_sqlite_string_literal( dest_literal)
44- ) ) ?;
45- Ok ( ( ) )
46- }
47-
4840fn hash_database_path_with_encryption (
4941 path : & Path ,
5042 encryption_opts : Option < EncryptionOpts > ,
@@ -70,7 +62,7 @@ fn can_read_query_from_uri(uri: &str, query: &str) -> bool {
7062}
7163
7264#[ derive( Clone , Copy ) ]
73- enum VacuumIntoTargetLiteral {
65+ enum VacuumIntoTarget {
7466 Plain ,
7567 Uri { query : & ' static str } ,
7668}
@@ -83,14 +75,14 @@ enum VacuumIntoDestinationSeed {
8375 ExistingDb ,
8476}
8577
86- fn sqlite_vacuum_literal ( path : & Path , target : VacuumIntoTargetLiteral ) -> String {
78+ fn build_vacuum_target ( path : & Path , target : VacuumIntoTarget ) -> String {
8779 let path = path
8880 . to_str ( )
8981 . expect ( "test database paths must be valid UTF-8" ) ;
9082 match target {
91- VacuumIntoTargetLiteral :: Plain => path. to_string ( ) ,
92- VacuumIntoTargetLiteral :: Uri { query : "" } => format ! ( "file:{path}" ) ,
93- VacuumIntoTargetLiteral :: Uri { query } => format ! ( "file:{path}?{query}" ) ,
83+ VacuumIntoTarget :: Plain => path. to_string ( ) ,
84+ VacuumIntoTarget :: Uri { query : "" } => format ! ( "file:{path}" ) ,
85+ VacuumIntoTarget :: Uri { query } => format ! ( "file:{path}?{query}" ) ,
9486 }
9587}
9688
@@ -1677,13 +1669,28 @@ fn test_connection_from_uri_memory_mode_opens_in_memory_db(
16771669 Ok ( ( ) )
16781670}
16791671
1672+ #[ turso_macros:: test]
1673+ fn test_connection_from_uri_memory_rw_mode_opens_in_memory_db (
1674+ _tmp_db : TempDatabase ,
1675+ ) -> anyhow:: Result < ( ) > {
1676+ let ( _io, conn) = Connection :: from_uri ( "file::memory:?mode=rw" , DatabaseOpts :: new ( ) ) ?;
1677+
1678+ conn. execute ( "CREATE TABLE t(value TEXT)" ) ?;
1679+ conn. execute ( "INSERT INTO t VALUES ('ok')" ) ?;
1680+
1681+ let rows: Vec < ( String , ) > = conn. exec_rows ( "SELECT value FROM t" ) ;
1682+ assert_eq ! ( rows, vec![ ( "ok" . to_string( ) , ) ] ) ;
1683+
1684+ Ok ( ( ) )
1685+ }
1686+
16801687#[ turso_macros:: test]
16811688fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite (
16821689 _tmp_db : TempDatabase ,
16831690) -> anyhow:: Result < ( ) > {
16841691 struct Case {
16851692 name : & ' static str ,
1686- target : VacuumIntoTargetLiteral ,
1693+ target : VacuumIntoTarget ,
16871694 seed : VacuumIntoDestinationSeed ,
16881695 expected_ok : bool ,
16891696 writes_file : bool ,
@@ -1692,98 +1699,98 @@ fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
16921699 let cases = [
16931700 Case {
16941701 name : "plain-existing-empty" ,
1695- target : VacuumIntoTargetLiteral :: Plain ,
1702+ target : VacuumIntoTarget :: Plain ,
16961703 seed : VacuumIntoDestinationSeed :: EmptyFile ,
16971704 expected_ok : false ,
16981705 writes_file : true ,
16991706 } ,
17001707 Case {
17011708 name : "uri-default-existing-empty" ,
1702- target : VacuumIntoTargetLiteral :: Uri { query : "" } ,
1709+ target : VacuumIntoTarget :: Uri { query : "" } ,
17031710 seed : VacuumIntoDestinationSeed :: EmptyFile ,
17041711 expected_ok : true ,
17051712 writes_file : true ,
17061713 } ,
17071714 Case {
17081715 name : "uri-default-existing-db" ,
1709- target : VacuumIntoTargetLiteral :: Uri { query : "" } ,
1716+ target : VacuumIntoTarget :: Uri { query : "" } ,
17101717 seed : VacuumIntoDestinationSeed :: ExistingDb ,
17111718 expected_ok : false ,
17121719 writes_file : true ,
17131720 } ,
17141721 Case {
17151722 name : "uri-default-existing-file" ,
1716- target : VacuumIntoTargetLiteral :: Uri { query : "" } ,
1723+ target : VacuumIntoTarget :: Uri { query : "" } ,
17171724 seed : VacuumIntoDestinationSeed :: ArbitraryFile ,
17181725 expected_ok : false ,
17191726 writes_file : true ,
17201727 } ,
17211728 Case {
17221729 name : "uri-rw-existing-empty" ,
1723- target : VacuumIntoTargetLiteral :: Uri { query : "mode=rw" } ,
1730+ target : VacuumIntoTarget :: Uri { query : "mode=rw" } ,
17241731 seed : VacuumIntoDestinationSeed :: EmptyFile ,
17251732 expected_ok : true ,
17261733 writes_file : true ,
17271734 } ,
17281735 Case {
17291736 name : "uri-rw-existing-db" ,
1730- target : VacuumIntoTargetLiteral :: Uri { query : "mode=rw" } ,
1737+ target : VacuumIntoTarget :: Uri { query : "mode=rw" } ,
17311738 seed : VacuumIntoDestinationSeed :: ExistingDb ,
17321739 expected_ok : false ,
17331740 writes_file : true ,
17341741 } ,
17351742 Case {
17361743 name : "uri-rw-existing-file" ,
1737- target : VacuumIntoTargetLiteral :: Uri { query : "mode=rw" } ,
1744+ target : VacuumIntoTarget :: Uri { query : "mode=rw" } ,
17381745 seed : VacuumIntoDestinationSeed :: ArbitraryFile ,
17391746 expected_ok : false ,
17401747 writes_file : true ,
17411748 } ,
17421749 Case {
17431750 name : "uri-rw-missing" ,
1744- target : VacuumIntoTargetLiteral :: Uri { query : "mode=rw" } ,
1751+ target : VacuumIntoTarget :: Uri { query : "mode=rw" } ,
17451752 seed : VacuumIntoDestinationSeed :: Missing ,
17461753 expected_ok : false ,
17471754 writes_file : true ,
17481755 } ,
17491756 Case {
17501757 name : "uri-rwc-existing-empty" ,
1751- target : VacuumIntoTargetLiteral :: Uri { query : "mode=rwc" } ,
1758+ target : VacuumIntoTarget :: Uri { query : "mode=rwc" } ,
17521759 seed : VacuumIntoDestinationSeed :: EmptyFile ,
17531760 expected_ok : true ,
17541761 writes_file : true ,
17551762 } ,
17561763 Case {
17571764 name : "uri-rwc-existing-db" ,
1758- target : VacuumIntoTargetLiteral :: Uri { query : "mode=rwc" } ,
1765+ target : VacuumIntoTarget :: Uri { query : "mode=rwc" } ,
17591766 seed : VacuumIntoDestinationSeed :: ExistingDb ,
17601767 expected_ok : false ,
17611768 writes_file : true ,
17621769 } ,
17631770 Case {
17641771 name : "uri-rwc-existing-file" ,
1765- target : VacuumIntoTargetLiteral :: Uri { query : "mode=rwc" } ,
1772+ target : VacuumIntoTarget :: Uri { query : "mode=rwc" } ,
17661773 seed : VacuumIntoDestinationSeed :: ArbitraryFile ,
17671774 expected_ok : false ,
17681775 writes_file : true ,
17691776 } ,
17701777 Case {
17711778 name : "uri-rwc-missing" ,
1772- target : VacuumIntoTargetLiteral :: Uri { query : "mode=rwc" } ,
1779+ target : VacuumIntoTarget :: Uri { query : "mode=rwc" } ,
17731780 seed : VacuumIntoDestinationSeed :: Missing ,
17741781 expected_ok : true ,
17751782 writes_file : true ,
17761783 } ,
17771784 Case {
17781785 name : "uri-ro-existing-empty" ,
1779- target : VacuumIntoTargetLiteral :: Uri { query : "mode=ro" } ,
1786+ target : VacuumIntoTarget :: Uri { query : "mode=ro" } ,
17801787 seed : VacuumIntoDestinationSeed :: EmptyFile ,
17811788 expected_ok : false ,
17821789 writes_file : true ,
17831790 } ,
17841791 Case {
17851792 name : "uri-memory-missing" ,
1786- target : VacuumIntoTargetLiteral :: Uri {
1793+ target : VacuumIntoTarget :: Uri {
17871794 query : "mode=memory" ,
17881795 } ,
17891796 seed : VacuumIntoDestinationSeed :: Missing ,
@@ -1792,7 +1799,7 @@ fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
17921799 } ,
17931800 Case {
17941801 name : "uri-memory-existing-file" ,
1795- target : VacuumIntoTargetLiteral :: Uri {
1802+ target : VacuumIntoTarget :: Uri {
17961803 query : "mode=memory" ,
17971804 } ,
17981805 seed : VacuumIntoDestinationSeed :: ArbitraryFile ,
@@ -1814,22 +1821,32 @@ fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
18141821 . exists ( )
18151822 . then ( || std:: fs:: read ( & turso_dest) )
18161823 . transpose ( ) ?;
1817- let turso_literal = sqlite_vacuum_literal ( & turso_dest, case. target ) ;
1824+ let vacuum_target = build_vacuum_target ( & turso_dest, case. target ) ;
18181825 let turso_ok = {
18191826 let turso_source_db = TempDatabase :: new_with_existent ( & turso_source) ;
18201827 let conn = turso_source_db. connect_limbo ( ) ;
1821- vacuum_into_literal ( & conn, & turso_literal) . is_ok ( )
1828+ conn. execute ( format ! (
1829+ "VACUUM INTO '{}'" ,
1830+ escape_sqlite_string_literal( & vacuum_target)
1831+ ) )
1832+ . is_ok ( )
18221833 } ;
18231834
18241835 assert_eq ! (
18251836 turso_ok, case. expected_ok,
18261837 "{}: VACUUM INTO result should match the sqlite3 3.51 runtime for {}" ,
1827- case. name, turso_literal
1838+ case. name, vacuum_target
18281839 ) ;
18291840
18301841 if turso_ok && case. writes_file {
1831- let source_hash = hash_database_path_with_encryption ( & turso_source, None ) ?;
1832- let dest_hash = hash_database_path_with_encryption ( & turso_dest, None ) ?;
1842+ let source_hash = turso_dbhash:: hash_database (
1843+ turso_source. to_str ( ) . unwrap ( ) ,
1844+ & turso_dbhash:: DbHashOptions :: default ( ) ,
1845+ ) ?;
1846+ let dest_hash = turso_dbhash:: hash_database (
1847+ turso_dest. to_str ( ) . unwrap ( ) ,
1848+ & turso_dbhash:: DbHashOptions :: default ( ) ,
1849+ ) ?;
18331850 assert_eq ! (
18341851 source_hash. hash, dest_hash. hash,
18351852 "{}: successful URI VACUUM should preserve logical content" ,
@@ -1878,7 +1895,7 @@ fn test_vacuum_into_file_uri_open_mode_parity_with_sqlite(
18781895}
18791896
18801897#[ turso_macros:: test]
1881- fn test_vacuum_into_uri_encrypts_plain_destination ( tmp_db : TempDatabase ) -> anyhow:: Result < ( ) > {
1898+ fn test_vacuum_into_uri_encrypts_plaintext_source ( tmp_db : TempDatabase ) -> anyhow:: Result < ( ) > {
18821899 let conn = tmp_db. connect_limbo ( ) ;
18831900 conn. execute ( "CREATE TABLE secrets (id INTEGER PRIMARY KEY, value TEXT)" ) ?;
18841901 conn. execute ( "INSERT INTO secrets VALUES (1, 'plain')" ) ?;
@@ -1898,7 +1915,10 @@ fn test_vacuum_into_uri_encrypts_plain_destination(tmp_db: TempDatabase) -> anyh
18981915 ) ;
18991916 let plain_uri = format ! ( "file:{dest_path_str}" ) ;
19001917
1901- vacuum_into_literal ( & conn, & dest_uri) ?;
1918+ conn. execute ( format ! (
1919+ "VACUUM INTO '{}'" ,
1920+ escape_sqlite_string_literal( & dest_uri)
1921+ ) ) ?;
19021922
19031923 assert ! (
19041924 !can_read_query_from_uri( & plain_uri, "SELECT COUNT(*) FROM secrets" ) ,
@@ -1948,15 +1968,21 @@ fn test_vacuum_into_uri_reencrypts_encrypted_source(_tmp_db: TempDatabase) -> an
19481968 "file:{dest_path_str}?cipher={}&hexkey={}" ,
19491969 dest_encryption_opts. cipher, dest_encryption_opts. hexkey
19501970 ) ;
1951- let source_uri = format ! (
1971+ let dest_uri_with_source_encryption_params = format ! (
19521972 "file:{dest_path_str}?cipher={}&hexkey={}" ,
19531973 source_encryption_opts. cipher, source_encryption_opts. hexkey
19541974 ) ;
19551975
1956- vacuum_into_literal ( & conn, & dest_uri) ?;
1976+ conn. execute ( format ! (
1977+ "VACUUM INTO '{}'" ,
1978+ escape_sqlite_string_literal( & dest_uri)
1979+ ) ) ?;
19571980
19581981 assert ! (
1959- !can_read_query_from_uri( & source_uri, "SELECT COUNT(*) FROM secrets" ) ,
1982+ !can_read_query_from_uri(
1983+ & dest_uri_with_source_encryption_params,
1984+ "SELECT COUNT(*) FROM secrets"
1985+ ) ,
19601986 "Destination should not be readable with the source encryption params"
19611987 ) ;
19621988 assert ! (
@@ -1996,14 +2022,20 @@ fn test_vacuum_into_uri_decrypts_encrypted_source(_tmp_db: TempDatabase) -> anyh
19962022 let dest_path = dest_dir. path ( ) . join ( "vacuum-plain.db" ) ;
19972023 let dest_uri = format ! ( "file:{}" , dest_path. to_str( ) . unwrap( ) ) ;
19982024
1999- vacuum_into_literal ( & conn, & dest_uri) ?;
2025+ conn. execute ( format ! (
2026+ "VACUUM INTO '{}'" ,
2027+ escape_sqlite_string_literal( & dest_uri)
2028+ ) ) ?;
20002029
20012030 assert ! (
20022031 can_read_query_from_uri( & dest_uri, "SELECT COUNT(*) FROM secrets" ) ,
20032032 "Plain VACUUM destination should be readable without encryption params"
20042033 ) ;
20052034
2006- let dest_hash = hash_database_path_with_encryption ( & dest_path, None ) ?;
2035+ let dest_hash = turso_dbhash:: hash_database (
2036+ dest_path. to_str ( ) . unwrap ( ) ,
2037+ & turso_dbhash:: DbHashOptions :: default ( ) ,
2038+ ) ?;
20072039 assert_eq ! (
20082040 source_hash. hash, dest_hash. hash,
20092041 "Plain VACUUM destination should preserve logical content"
0 commit comments