diff --git a/sqlx-sqlite/Cargo.toml b/sqlx-sqlite/Cargo.toml index 80a03ca0b..1ad87de10 100644 --- a/sqlx-sqlite/Cargo.toml +++ b/sqlx-sqlite/Cargo.toml @@ -63,7 +63,7 @@ features = [ workspace = true [dev-dependencies] -sqlx = { workspace = true, default-features = false, features = ["macros", "runtime-tokio", "tls-none"] } +sqlx = { workspace = true, default-features = false, features = ["macros", "runtime-tokio", "tls-none", "sqlite"] } [lints] workspace = true diff --git a/sqlx-sqlite/src/connection/explain.rs b/sqlx-sqlite/src/connection/explain.rs index 89762d171..bfa66aa12 100644 --- a/sqlx-sqlite/src/connection/explain.rs +++ b/sqlx-sqlite/src/connection/explain.rs @@ -1633,147 +1633,147 @@ fn test_root_block_columns_has_types() { { let table_db_block = table_block_nums["t"]; assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Integer, nullable: Some(true) //sqlite primary key columns are nullable unless declared not null - }, - root_block_cols[&table_db_block][&0] + }), + root_block_cols[&table_db_block].get(&0) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Text, nullable: Some(true) - }, - root_block_cols[&table_db_block][&1] + }), + root_block_cols[&table_db_block].get(&1) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Text, nullable: Some(false) - }, - root_block_cols[&table_db_block][&2] + }), + root_block_cols[&table_db_block].get(&2) ); } { let table_db_block = table_block_nums["i1"]; assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Integer, nullable: Some(true) //sqlite primary key columns are nullable unless declared not null - }, - root_block_cols[&table_db_block][&0] + }), + root_block_cols[&table_db_block].get(&0) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Text, nullable: Some(true) - }, - root_block_cols[&table_db_block][&1] + }), + root_block_cols[&table_db_block].get(&1) ); } { let table_db_block = table_block_nums["i2"]; assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Integer, nullable: Some(true) //sqlite primary key columns are nullable unless declared not null - }, - root_block_cols[&table_db_block][&0] + }), + root_block_cols[&table_db_block].get(&0) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Text, nullable: Some(true) - }, - root_block_cols[&table_db_block][&1] + }), + root_block_cols[&table_db_block].get(&1) ); } { let table_db_block = table_block_nums["t2"]; assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Integer, nullable: Some(false) - }, - root_block_cols[&table_db_block][&0] + }), + root_block_cols[&table_db_block].get(&0) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Null, nullable: Some(true) - }, - root_block_cols[&table_db_block][&1] + }), + root_block_cols[&table_db_block].get(&1) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Null, nullable: Some(false) - }, - root_block_cols[&table_db_block][&2] + }), + root_block_cols[&table_db_block].get(&2) ); } { let table_db_block = table_block_nums["t2i1"]; assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Integer, nullable: Some(false) - }, - root_block_cols[&table_db_block][&0] + }), + root_block_cols[&table_db_block].get(&0) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Null, nullable: Some(true) - }, - root_block_cols[&table_db_block][&1] + }), + root_block_cols[&table_db_block].get(&1) ); } { let table_db_block = table_block_nums["t2i2"]; assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Integer, nullable: Some(false) - }, - root_block_cols[&table_db_block][&0] + }), + root_block_cols[&table_db_block].get(&0) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Null, nullable: Some(false) - }, - root_block_cols[&table_db_block][&1] + }), + root_block_cols[&table_db_block].get(&1) ); } { let table_db_block = table_block_nums["t3"]; assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Text, nullable: Some(true) - }, - root_block_cols[&table_db_block][&0] + }), + root_block_cols[&table_db_block].get(&0) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Float, nullable: Some(false) - }, - root_block_cols[&table_db_block][&1] + }), + root_block_cols[&table_db_block].get(&1) ); assert_eq!( - ColumnType::Single { + Some(&ColumnType::Single { datatype: DataType::Float, nullable: Some(true) - }, - root_block_cols[&table_db_block][&2] + }), + root_block_cols[&table_db_block].get(&2) ); } } diff --git a/sqlx-sqlite/src/options/parse.rs b/sqlx-sqlite/src/options/parse.rs index f06cf0c65..0530f4204 100644 --- a/sqlx-sqlite/src/options/parse.rs +++ b/sqlx-sqlite/src/options/parse.rs @@ -1,12 +1,14 @@ -use crate::error::Error; -use crate::SqliteConnectOptions; -use percent_encoding::{percent_decode_str, utf8_percent_encode, NON_ALPHANUMERIC}; use std::borrow::Cow; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::atomic::{AtomicUsize, Ordering}; + +use percent_encoding::{percent_decode_str, percent_encode, AsciiSet}; use url::Url; +use crate::error::Error; +use crate::SqliteConnectOptions; + // https://www.sqlite.org/uri.html static IN_MEMORY_DB_SEQ: AtomicUsize = AtomicUsize::new(0); @@ -114,10 +116,25 @@ impl SqliteConnectOptions { } pub(crate) fn build_url(&self) -> Url { - let filename = - utf8_percent_encode(&self.filename.to_string_lossy(), NON_ALPHANUMERIC).to_string(); - let mut url = - Url::parse(&format!("sqlite://{}", filename)).expect("BUG: generated un-parseable URL"); + // https://url.spec.whatwg.org/#path-percent-encode-set + static PATH_ENCODE_SET: AsciiSet = percent_encoding::CONTROLS + .add(b' ') + .add(b'"') + .add(b'#') + .add(b'<') + .add(b'>') + .add(b'?') + .add(b'`') + .add(b'{') + .add(b'}'); + + let filename_encoded = percent_encode( + self.filename.as_os_str().as_encoded_bytes(), + &PATH_ENCODE_SET, + ); + + let mut url = Url::parse(&format!("sqlite://{filename_encoded}")) + .expect("BUG: generated un-parseable URL"); let mode = match (self.in_memory, self.create_if_missing, self.read_only) { (true, _, _) => "memory", @@ -133,8 +150,9 @@ impl SqliteConnectOptions { }; url.query_pairs_mut().append_pair("cache", cache); - url.query_pairs_mut() - .append_pair("immutable", &self.immutable.to_string()); + if self.immutable { + url.query_pairs_mut().append_pair("immutable", "true"); + } if let Some(vfs) = &self.vfs { url.query_pairs_mut().append_pair("vfs", vfs); diff --git a/sqlx-sqlite/src/regexp.rs b/sqlx-sqlite/src/regexp.rs index ee19482ee..eb14fffc7 100644 --- a/sqlx-sqlite/src/regexp.rs +++ b/sqlx-sqlite/src/regexp.rs @@ -170,7 +170,7 @@ unsafe extern "C" fn cleanup_arc_regex_pointer(ptr: *mut std::ffi::c_void) { #[cfg(test)] mod tests { - use sqlx::{ConnectOptions, Connection, Row}; + use sqlx::{ConnectOptions, Row}; use std::str::FromStr; async fn test_db() -> crate::SqliteConnection {