diff options
author | Andrew Walbran <qwandor@google.com> | 2023-05-30 22:54:37 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-05-30 22:54:37 +0000 |
commit | f82680bac827c69f45fd3b7dc9467910fc0e8e2a (patch) | |
tree | f16f7b4a65385063acd5c2e6ad3b8a01f47d6411 | |
parent | 3a1d3e99ec7390190fa5aa997fd13dae5f19c942 (diff) | |
parent | 710a98d1f5c07c1f0c5c03f2a1f825d6f41bd55a (diff) | |
download | rusqlite-f82680bac827c69f45fd3b7dc9467910fc0e8e2a.tar.gz |
Update to 0.29.0. am: 3de7850ae0 am: e576f2428a am: 4c9427463d am: 105a1fe1ca am: bc9c9a01f7 am: 710a98d1f5
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/rusqlite/+/2605765
Change-Id: I40213f4feb409910e59f2d6348a5db85cc5d557f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
45 files changed, 603 insertions, 840 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 96ed8b8..02fc5c1 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "26293a11f595574897e7e5a5b639d1587255c6b9" + "sha1": "a1ef4b5b6d647d907810b7e15db34f460abd8ff7" }, "path_in_vcs": "" }
\ No newline at end of file @@ -23,7 +23,7 @@ rust_library { host_supported: true, crate_name: "rusqlite", cargo_env_compat: true, - cargo_pkg_version: "0.28.0", + cargo_pkg_version: "0.29.0", srcs: ["src/lib.rs"], edition: "2018", features: [ @@ -31,7 +31,7 @@ rust_library { "trace", ], rustlibs: [ - "libbitflags-1.3.2", + "libbitflags", "libfallible_iterator", "libfallible_streaming_iterator", "libhashlink", @@ -12,7 +12,7 @@ [package] edition = "2018" name = "rusqlite" -version = "0.28.0" +version = "0.29.0" authors = ["The rusqlite developers"] exclude = [ "/.github/*", @@ -35,18 +35,18 @@ license = "MIT" repository = "https://github.com/rusqlite/rusqlite" [package.metadata.docs.rs] -features = ["modern-full"] all-features = false -no-default-features = true default-target = "x86_64-unknown-linux-gnu" +features = ["modern-full"] +no-default-features = true rustdoc-args = [ "--cfg", "docsrs", ] [package.metadata.playground] -features = ["bundled-full"] all-features = false +features = ["bundled-full"] [lib] name = "rusqlite" @@ -70,7 +70,7 @@ name = "exec" harness = false [dependencies.bitflags] -version = "1.2" +version = "2.0" [dependencies.chrono] version = "0.4" @@ -91,12 +91,8 @@ version = "0.1" [dependencies.hashlink] version = "0.8" -[dependencies.lazy_static] -version = "1.4" -optional = true - [dependencies.libsqlite3-sys] -version = "0.25.0" +version = "0.26.0" [dependencies.serde_json] version = "1.0" @@ -146,8 +142,8 @@ features = ["v4"] [features] array = ["vtab"] -backup = ["libsqlite3-sys/min_sqlite_version_3_6_23"] -blob = ["libsqlite3-sys/min_sqlite_version_3_7_7"] +backup = [] +blob = [] buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"] bundled = [ "libsqlite3-sys/bundled", @@ -173,7 +169,7 @@ csvtab = [ "vtab", ] extra_check = [] -functions = ["libsqlite3-sys/min_sqlite_version_3_7_7"] +functions = [] hooks = [] i128_blob = [] in_gecko = [ @@ -208,16 +204,16 @@ modern-full = [ "window", ] modern_sqlite = ["libsqlite3-sys/bundled_bindings"] -release_memory = ["libsqlite3-sys/min_sqlite_version_3_7_16"] +release_memory = [] series = ["vtab"] session = [ "libsqlite3-sys/session", "hooks", ] sqlcipher = ["libsqlite3-sys/sqlcipher"] -trace = ["libsqlite3-sys/min_sqlite_version_3_6_23"] +trace = [] unlock_notify = ["libsqlite3-sys/unlock_notify"] -vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7"] +vtab = [] wasm32-wasi-vfs = ["libsqlite3-sys/wasm32-wasi-vfs"] window = ["functions"] winsqlite3 = ["libsqlite3-sys/winsqlite3"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index bd81d44..7d79d7b 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,7 +1,7 @@ [package] name = "rusqlite" # Note: Update version in README.md when you change this. -version = "0.28.0" +version = "0.29.0" authors = ["The rusqlite developers"] edition = "2018" description = "Ergonomic wrapper for SQLite" @@ -35,16 +35,16 @@ members = ["libsqlite3-sys"] [features] load_extension = [] # hot-backup interface: 3.6.11 (2009-02-18) -backup = ["libsqlite3-sys/min_sqlite_version_3_6_23"] +backup = [] # sqlite3_blob_reopen: 3.7.4 -blob = ["libsqlite3-sys/min_sqlite_version_3_7_7"] +blob = [] collation = [] # sqlite3_create_function_v2: 3.7.3 (2010-10-08) -functions = ["libsqlite3-sys/min_sqlite_version_3_7_7"] +functions = [] # sqlite3_log: 3.6.23 (2010-03-09) -trace = ["libsqlite3-sys/min_sqlite_version_3_6_23"] +trace = [] # sqlite3_db_release_memory: 3.7.10 (2012-01-16) -release_memory = ["libsqlite3-sys/min_sqlite_version_3_7_16"] +release_memory = [] bundled = ["libsqlite3-sys/bundled", "modern_sqlite"] bundled-sqlcipher = ["libsqlite3-sys/bundled-sqlcipher", "bundled"] bundled-sqlcipher-vendored-openssl = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl", "bundled-sqlcipher"] @@ -55,7 +55,7 @@ i128_blob = [] sqlcipher = ["libsqlite3-sys/sqlcipher"] unlock_notify = ["libsqlite3-sys/unlock_notify"] # xSavepoint, xRelease and xRollbackTo: 3.7.7 (2011-06-23) -vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7"] +vtab = [] csvtab = ["csv", "vtab"] # pointer passing interfaces: 3.20.0 array = ["vtab"] @@ -67,6 +67,7 @@ window = ["functions"] series = ["vtab"] # check for invalid query. extra_check = [] +# ]3.14.0, last] modern_sqlite = ["libsqlite3-sys/bundled_bindings"] in_gecko = ["modern_sqlite", "libsqlite3-sys/in_gecko"] bundled-windows = ["libsqlite3-sys/bundled-windows"] @@ -111,13 +112,12 @@ bundled-full = ["modern-full", "bundled"] [dependencies] time = { version = "0.3.0", features = ["formatting", "macros", "parsing"], optional = true } -bitflags = "1.2" +bitflags = "2.0" hashlink = "0.8" chrono = { version = "0.4", optional = true, default-features = false, features = ["clock"] } serde_json = { version = "1.0", optional = true } csv = { version = "1.1", optional = true } url = { version = "2.1", optional = true } -lazy_static = { version = "1.4", optional = true } fallible-iterator = "0.2" fallible-streaming-iterator = "0.1" uuid = { version = "1.0", optional = true } @@ -136,7 +136,7 @@ bencher = "0.1" [dependencies.libsqlite3-sys] path = "libsqlite3-sys" -version = "0.25.0" +version = "0.26.0" [[test]] name = "config_log" @@ -11,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/rusqlite/rusqlite-0.28.0.crate" + value: "https://static.crates.io/crates/rusqlite/rusqlite-0.29.0.crate" } - version: "0.28.0" + version: "0.29.0" license_type: NOTICE last_upgrade_date { - year: 2022 - month: 12 - day: 13 + year: 2023 + month: 5 + day: 25 } } @@ -27,7 +27,7 @@ In your Cargo.toml: # That said, it's not ideal for all scenarios and in particular, generic # libraries built around `rusqlite` should probably not enable it, which # is why it is not a default feature -- it could become hard to disable. -rusqlite = { version = "0.28.0", features = ["bundled"] } +rusqlite = { version = "0.29.0", features = ["bundled"] } ``` Simple example usage: @@ -81,7 +81,7 @@ fn main() -> Result<()> { ### Supported SQLite Versions -The base `rusqlite` package supports SQLite version 3.6.8 or newer. If you need +The base `rusqlite` package supports SQLite version 3.14.0 or newer. If you need support for older versions, please file an issue. Some cargo features require a newer SQLite version; see details below. @@ -149,11 +149,11 @@ You can adjust this behavior in a number of ways: * If you use the `bundled`, `bundled-sqlcipher`, or `bundled-sqlcipher-vendored-openssl` features, `libsqlite3-sys` will use the [cc](https://crates.io/crates/cc) crate to compile SQLite or SQLCipher from source and link against that. This source is embedded in the `libsqlite3-sys` crate and - is currently SQLite 3.39.0 (as of `rusqlite` 0.28.0 / `libsqlite3-sys` - 0.25.0). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file: + is currently SQLite 3.41.2 (as of `rusqlite` 0.29.0 / `libsqlite3-sys` + 0.26.0). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file: ```toml [dependencies.rusqlite] - version = "0.28.0" + version = "0.29.0" features = ["bundled"] ``` * When using any of the `bundled` features, the build script will honor `SQLITE_MAX_VARIABLE_NUMBER` and `SQLITE_MAX_EXPR_DEPTH` variables. It will also honor a `LIBSQLITE3_FLAGS` variable, which can have a format like `"-USQLITE_ALPHA -DSQLITE_BETA SQLITE_GAMMA ..."`. That would disable the `SQLITE_ALPHA` flag, and set the `SQLITE_BETA` and `SQLITE_GAMMA` flags. (The initial `-D` can be omitted, as on the last one.) @@ -191,9 +191,7 @@ minimum SQLite version that supports your chosen features. If you are using `libsqlite3-sys` directly, you can use the same features to choose which pregenerated bindings are chosen: -* `min_sqlite_version_3_6_8` - SQLite 3.6.8 bindings (this is the default) -* `min_sqlite_version_3_6_23` - SQLite 3.6.23 bindings -* `min_sqlite_version_3_7_7` - SQLite 3.7.7 bindings +* `min_sqlite_version_3_14_0` - SQLite 3.14.0 bindings (this is the default) If you use any of the `bundled` features, you will get pregenerated bindings for the bundled version of SQLite/SQLCipher. If you need other specific pregenerated binding diff --git a/patches/Android.bp.diff b/patches/Android.bp.diff deleted file mode 100644 index 34d5397..0000000 --- a/patches/Android.bp.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/Android.bp b/Android.bp -index 51830a3..ca704b4 100644 ---- a/Android.bp -+++ b/Android.bp -@@ -31,7 +31,7 @@ rust_library { - "trace", - ], - rustlibs: [ -- "libbitflags", -+ "libbitflags-1.3.2", - "libfallible_iterator", - "libfallible_streaming_iterator", - "libhashlink", diff --git a/src/backup.rs b/src/backup.rs index 6da01fd..f28ae9a 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -336,7 +336,7 @@ mod test { backup.step(-1)?; } - let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?; + let the_answer: i64 = dst.one_column("SELECT x FROM foo")?; assert_eq!(42, the_answer); src.execute_batch("INSERT INTO foo VALUES(43)")?; @@ -346,7 +346,7 @@ mod test { backup.run_to_completion(5, Duration::from_millis(250), None)?; } - let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?; + let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?; assert_eq!(42 + 43, the_answer); Ok(()) } @@ -368,7 +368,7 @@ mod test { backup.step(-1)?; } - let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?; + let the_answer: i64 = dst.one_column("SELECT x FROM foo")?; assert_eq!(42, the_answer); src.execute_batch("INSERT INTO foo VALUES(43)")?; @@ -379,7 +379,7 @@ mod test { backup.run_to_completion(5, Duration::from_millis(250), None)?; } - let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?; + let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?; assert_eq!(42 + 43, the_answer); Ok(()) } @@ -406,7 +406,7 @@ mod test { backup.step(-1)?; } - let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?; + let the_answer: i64 = dst.one_column("SELECT x FROM foo")?; assert_eq!(42, the_answer); src.execute_batch("INSERT INTO foo VALUES(43)")?; @@ -421,7 +421,7 @@ mod test { backup.run_to_completion(5, Duration::from_millis(250), None)?; } - let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?; + let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?; assert_eq!(42 + 43, the_answer); Ok(()) } diff --git a/src/blob/mod.rs b/src/blob/mod.rs index 81c6098..46e3ea8 100644 --- a/src/blob/mod.rs +++ b/src/blob/mod.rs @@ -134,7 +134,7 @@ //! // Insert another BLOB, this time using a parameter passed in from //! // rust (potentially with a dynamic size). //! db.execute( -//! "INSERT INTO test_table (content) VALUES (?)", +//! "INSERT INTO test_table (content) VALUES (?1)", //! [ZeroBlob(64)], //! )?; //! @@ -175,7 +175,7 @@ //! // Insert another blob, this time using a parameter passed in from //! // rust (potentially with a dynamic size). //! db.execute( -//! "INSERT INTO test_table (content) VALUES (?)", +//! "INSERT INTO test_table (content) VALUES (?1)", //! [ZeroBlob(64)], //! )?; //! @@ -235,7 +235,7 @@ impl Connection { table.as_ptr(), column.as_ptr(), row_id, - if read_only { 0 } else { 1 }, + !read_only as std::os::raw::c_int, &mut blob, ) }; @@ -473,14 +473,14 @@ mod test { assert_eq!(&bytes, b"Clob5"); // should not be able to seek negative or past end - assert!(blob.seek(SeekFrom::Current(-20)).is_err()); - assert!(blob.seek(SeekFrom::End(0)).is_ok()); - assert!(blob.seek(SeekFrom::Current(1)).is_err()); + blob.seek(SeekFrom::Current(-20)).unwrap_err(); + blob.seek(SeekFrom::End(0)).unwrap(); + blob.seek(SeekFrom::Current(1)).unwrap_err(); // write_all should detect when we return Ok(0) because there is no space left, // and return a write error blob.reopen(rowid)?; - assert!(blob.write_all(b"0123456789x").is_err()); + blob.write_all(b"0123456789x").unwrap_err(); Ok(()) } @@ -519,7 +519,7 @@ mod test { // trying to write too much and then flush should fail assert_eq!(8, writer.write(b"01234567").unwrap()); assert_eq!(8, writer.write(b"01234567").unwrap()); - assert!(writer.flush().is_err()); + writer.flush().unwrap_err(); } { @@ -536,7 +536,7 @@ mod test { // trying to write_all too much should fail writer.write_all(b"aaaaaaaaaabbbbb").unwrap(); - assert!(writer.flush().is_err()); + writer.flush().unwrap_err(); } { diff --git a/src/blob/pos_io.rs b/src/blob/pos_io.rs index ecc7d65..d970ab7 100644 --- a/src/blob/pos_io.rs +++ b/src/blob/pos_io.rs @@ -214,7 +214,7 @@ mod test { let mut s = [0u8; 10]; blob.read_at_exact(&mut s, 0).unwrap(); assert_eq!(&s, &one2ten, "write should go through"); - assert!(blob.read_at_exact(&mut s, 1).is_err()); + blob.read_at_exact(&mut s, 1).unwrap_err(); blob.read_at_exact(&mut s, 0).unwrap(); assert_eq!(&s, &one2ten, "should be unchanged"); @@ -225,13 +225,13 @@ mod test { blob.read_at_exact(&mut fives, 5).unwrap(); assert_eq!(&fives, &[6u8, 7, 8, 9, 10]); - assert!(blob.read_at_exact(&mut fives, 7).is_err()); - assert!(blob.read_at_exact(&mut fives, 12).is_err()); - assert!(blob.read_at_exact(&mut fives, 10).is_err()); - assert!(blob.read_at_exact(&mut fives, i32::MAX as usize).is_err()); - assert!(blob - .read_at_exact(&mut fives, i32::MAX as usize + 1) - .is_err()); + blob.read_at_exact(&mut fives, 7).unwrap_err(); + blob.read_at_exact(&mut fives, 12).unwrap_err(); + blob.read_at_exact(&mut fives, 10).unwrap_err(); + blob.read_at_exact(&mut fives, i32::MAX as usize) + .unwrap_err(); + blob.read_at_exact(&mut fives, i32::MAX as usize + 1) + .unwrap_err(); // zero length writes are fine if in bounds blob.read_at_exact(&mut [], 10).unwrap(); @@ -242,13 +242,11 @@ mod test { blob.read_at_exact(&mut s, 0).unwrap(); assert_eq!(&s, &[1u8, 2, 3, 4, 5, 16, 17, 18, 19, 20]); - assert!(blob.write_at(&[100, 99, 98, 97, 96], 6).is_err()); - assert!(blob - .write_at(&[100, 99, 98, 97, 96], i32::MAX as usize) - .is_err()); - assert!(blob - .write_at(&[100, 99, 98, 97, 96], i32::MAX as usize + 1) - .is_err()); + blob.write_at(&[100, 99, 98, 97, 96], 6).unwrap_err(); + blob.write_at(&[100, 99, 98, 97, 96], i32::MAX as usize) + .unwrap_err(); + blob.write_at(&[100, 99, 98, 97, 96], i32::MAX as usize + 1) + .unwrap_err(); blob.read_at_exact(&mut s, 0).unwrap(); assert_eq!(&s, &[1u8, 2, 3, 4, 5, 16, 17, 18, 19, 20]); @@ -265,9 +263,9 @@ mod test { blob.raw_read_at_exact(&mut empty, 0).unwrap().as_ptr(), empty.as_ptr().cast(), )); - assert!(blob.raw_read_at_exact(&mut s2, 5).is_err()); + blob.raw_read_at_exact(&mut s2, 5).unwrap_err(); - let end_pos = blob.seek(std::io::SeekFrom::Current(0)).unwrap(); + let end_pos = blob.stream_position().unwrap(); assert_eq!(end_pos, 1); Ok(()) } diff --git a/src/busy.rs b/src/busy.rs index 7297f20..b9a5d40 100644 --- a/src/busy.rs +++ b/src/busy.rs @@ -58,11 +58,7 @@ impl Connection { pub fn busy_handler(&self, callback: Option<fn(i32) -> bool>) -> Result<()> { unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int { let handler_fn: fn(i32) -> bool = mem::transmute(p_arg); - if let Ok(true) = catch_unwind(|| handler_fn(count)) { - 1 - } else { - 0 - } + c_int::from(catch_unwind(|| handler_fn(count)).unwrap_or_default()) } let c = self.db.borrow_mut(); let r = match callback { diff --git a/src/cache.rs b/src/cache.rs index c80a708..be15268 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -17,13 +17,13 @@ impl Connection { /// # use rusqlite::{Connection, Result}; /// fn insert_new_people(conn: &Connection) -> Result<()> { /// { - /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?; + /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?; /// stmt.execute(["Joe Smith"])?; /// } /// { /// // This will return the same underlying SQLite statement handle without /// // having to prepare it again. - /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?; + /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?; /// stmt.execute(["Bob Jones"])?; /// } /// Ok(()) diff --git a/src/column.rs b/src/column.rs index aa1f5f7..4413a62 100644 --- a/src/column.rs +++ b/src/column.rs @@ -33,7 +33,7 @@ impl Statement<'_> { /// calling this method. pub fn column_names(&self) -> Vec<&str> { let n = self.column_count(); - let mut cols = Vec::with_capacity(n as usize); + let mut cols = Vec::with_capacity(n); for i in 0..n { let s = self.column_name_unwrap(i); cols.push(s); @@ -95,6 +95,7 @@ impl Statement<'_> { pub fn column_name(&self, col: usize) -> Result<&str> { self.stmt .column_name(col) + // clippy::or_fun_call (nightly) vs clippy::unnecessary-lazy-evaluations (stable) .ok_or(Error::InvalidColumnIndex(col)) .map(|slice| { str::from_utf8(slice.to_bytes()).expect("Invalid UTF-8 sequence in column name") @@ -137,7 +138,7 @@ impl Statement<'_> { #[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))] pub fn columns(&self) -> Vec<Column> { let n = self.column_count(); - let mut cols = Vec::with_capacity(n as usize); + let mut cols = Vec::with_capacity(n); for i in 0..n { let name = self.column_name_unwrap(i); let slice = self.stmt.column_decltype(i); diff --git a/src/config.rs b/src/config.rs index b295d97..d0fa41a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,12 +16,12 @@ pub enum DbConfig { //SQLITE_DBCONFIG_MAINDBNAME = 1000, /* const char* */ //SQLITE_DBCONFIG_LOOKASIDE = 1001, /* void* int int */ /// Enable or disable the enforcement of foreign key constraints. - SQLITE_DBCONFIG_ENABLE_FKEY = 1002, + SQLITE_DBCONFIG_ENABLE_FKEY = ffi::SQLITE_DBCONFIG_ENABLE_FKEY, /// Enable or disable triggers. - SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003, + SQLITE_DBCONFIG_ENABLE_TRIGGER = ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER, /// Enable or disable the fts3_tokenizer() function which is part of the /// FTS3 full-text search engine extension. - SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004, // 3.12.0 + SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, // 3.12.0 //SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005, /// In WAL mode, enable or disable the checkpoint operation before closing /// the connection. @@ -115,7 +115,7 @@ impl Connection { check(ffi::sqlite3_db_config( c.db(), config as c_int, - if new_val { 1 } else { 0 }, + new_val as c_int, &mut val, ))?; Ok(val != 0) diff --git a/src/error.rs b/src/error.rs index 3c264d3..797a216 100644 --- a/src/error.rs +++ b/src/error.rs @@ -245,7 +245,7 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Error::SqliteFailure(ref err, None) => err.fmt(f), - Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s), + Error::SqliteFailure(_, Some(ref s)) => write!(f, "{s}"), Error::SqliteSingleThreadedMode => write!( f, "SQLite was compiled or configured for single-threaded use only" @@ -263,21 +263,21 @@ impl fmt::Display for Error { } Error::IntegralValueOutOfRange(col, val) => { if col != UNKNOWN_COLUMN { - write!(f, "Integer {} out of range at index {}", val, col) + write!(f, "Integer {val} out of range at index {col}") } else { - write!(f, "Integer {} out of range", val) + write!(f, "Integer {val} out of range") } } Error::Utf8Error(ref err) => err.fmt(f), Error::NulError(ref err) => err.fmt(f), - Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name), + Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {name}"), Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()), Error::ExecuteReturnedResults => { write!(f, "Execute returned results - did you mean to call query?") } Error::QueryReturnedNoRows => write!(f, "Query returned no rows"), - Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i), - Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name), + Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"), + Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"), Error::InvalidColumnType(i, ref name, ref t) => write!( f, "Invalid column type {} at index: {}, name: {}", @@ -288,22 +288,22 @@ impl fmt::Display for Error { "Wrong number of parameters passed to query. Got {}, needed {}", i1, n1 ), - Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i), + Error::StatementChangedRows(i) => write!(f, "Query changed {i} rows"), #[cfg(feature = "functions")] Error::InvalidFunctionParameterType(i, ref t) => { - write!(f, "Invalid function parameter type {} at index {}", t, i) + write!(f, "Invalid function parameter type {t} at index {i}") } #[cfg(feature = "vtab")] Error::InvalidFilterParameterType(i, ref t) => { - write!(f, "Invalid filter parameter type {} at index {}", t, i) + write!(f, "Invalid filter parameter type {t} at index {i}") } #[cfg(feature = "functions")] Error::UserFunctionError(ref err) => err.fmt(f), Error::ToSqlConversionFailure(ref err) => err.fmt(f), Error::InvalidQuery => write!(f, "Query is not read-only"), #[cfg(feature = "vtab")] - Error::ModuleError(ref desc) => write!(f, "{}", desc), + Error::ModuleError(ref desc) => write!(f, "{desc}"), #[cfg(feature = "functions")] Error::UnwindingPanic => write!(f, "unwinding panic"), #[cfg(feature = "functions")] @@ -317,7 +317,7 @@ impl fmt::Display for Error { offset, ref sql, .. - } => write!(f, "{} in {} at offset {}", msg, sql, offset), + } => write!(f, "{msg} in {sql} at offset {offset}"), } } } @@ -408,13 +408,13 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error { } #[cold] -#[cfg(not(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher"))))] // SQLite >= 3.38.0 +#[cfg(not(feature = "modern_sqlite"))] // SQLite >= 3.38.0 pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error { error_from_handle(db, code) } #[cold] -#[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0 +#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0 pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error { if db.is_null() { error_from_sqlite_code(code, None) diff --git a/src/functions.rs b/src/functions.rs index 138baac..2ae29a8 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -75,14 +75,9 @@ unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) { // an explicit feature check for that, and this doesn't really warrant one. // We'll use the extended code if we're on the bundled version (since it's // at least 3.17.0) and the normal constraint error code if not. - #[cfg(feature = "modern_sqlite")] fn constraint_error_code() -> i32 { ffi::SQLITE_CONSTRAINT_FUNCTION } - #[cfg(not(feature = "modern_sqlite"))] - fn constraint_error_code() -> i32 { - ffi::SQLITE_CONSTRAINT - } if let Error::SqliteFailure(ref err, ref s) = *err { ffi::sqlite3_result_error_code(ctx, err.extended_code); @@ -168,8 +163,6 @@ impl Context<'_> { /// /// Will panic if `idx` is greater than or equal to /// [`self.len()`](Context::len). - #[cfg(feature = "modern_sqlite")] // 3.9.0 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] pub fn get_subtype(&self, idx: usize) -> std::os::raw::c_uint { let arg = self.args[idx]; unsafe { ffi::sqlite3_value_subtype(arg) } @@ -249,8 +242,6 @@ impl Context<'_> { } /// Set the Subtype of an SQL function - #[cfg(feature = "modern_sqlite")] // 3.9.0 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] pub fn set_result_subtype(&self, sub_type: std::os::raw::c_uint) { unsafe { ffi::sqlite3_result_subtype(self.ctx, sub_type) }; } @@ -829,9 +820,9 @@ mod test { FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, half, )?; - let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0)); + let result: f64 = db.one_column("SELECT half(6)")?; - assert!((3f64 - result?).abs() < f64::EPSILON); + assert!((3f64 - result).abs() < f64::EPSILON); Ok(()) } @@ -844,12 +835,12 @@ mod test { FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, half, )?; - let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0)); - assert!((3f64 - result?).abs() < f64::EPSILON); + let result: f64 = db.one_column("SELECT half(6)")?; + assert!((3f64 - result).abs() < f64::EPSILON); db.remove_function("half", 1)?; - let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0)); - assert!(result.is_err()); + let result: Result<f64> = db.one_column("SELECT half(6)"); + result.unwrap_err(); Ok(()) } @@ -894,18 +885,14 @@ mod test { regexp_with_auxilliary, )?; - let result: Result<bool> = - db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", [], |r| r.get(0)); + let result: bool = db.one_column("SELECT regexp('l.s[aeiouy]', 'lisa')")?; - assert!(result?); + assert!(result); - let result: Result<i64> = db.query_row( - "SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1", - [], - |r| r.get(0), - ); + let result: i64 = + db.one_column("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1")?; - assert_eq!(2, result?); + assert_eq!(2, result); Ok(()) } @@ -933,7 +920,7 @@ mod test { ("onetwo", "SELECT my_concat('one', 'two')"), ("abc", "SELECT my_concat('a', 'b', 'c')"), ] { - let result: String = db.query_row(query, [], |r| r.get(0))?; + let result: String = db.one_column(query)?; assert_eq!(expected, result); } Ok(()) @@ -952,11 +939,8 @@ mod test { Ok(true) })?; - let res: bool = db.query_row( - "SELECT example(0, i) FROM (SELECT 0 as i UNION SELECT 1)", - [], - |r| r.get(0), - )?; + let res: bool = + db.one_column("SELECT example(0, i) FROM (SELECT 0 as i UNION SELECT 1)")?; // Doesn't actually matter, we'll assert in the function if there's a problem. assert!(res); Ok(()) @@ -1007,11 +991,11 @@ mod test { // sum should return NULL when given no columns (contrast with count below) let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)"; - let result: Option<i64> = db.query_row(no_result, [], |r| r.get(0))?; + let result: Option<i64> = db.one_column(no_result)?; assert!(result.is_none()); let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; - let result: i64 = db.query_row(single_sum, [], |r| r.get(0))?; + let result: i64 = db.one_column(single_sum)?; assert_eq!(4, result); let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \ @@ -1033,11 +1017,11 @@ mod test { // count should return 0 when given no columns (contrast with sum above) let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)"; - let result: i64 = db.query_row(no_result, [], |r| r.get(0))?; + let result: i64 = db.one_column(no_result)?; assert_eq!(result, 0); let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; - let result: i64 = db.query_row(single_sum, [], |r| r.get(0))?; + let result: i64 = db.one_column(single_sum)?; assert_eq!(2, result); Ok(()) } diff --git a/src/hooks.rs b/src/hooks.rs index 5058a0c..7a87bc3 100644 --- a/src/hooks.rs +++ b/src/hooks.rs @@ -181,7 +181,6 @@ pub enum AuthAction<'c> { operation: TransactionOperation, savepoint_name: &'c str, }, - #[cfg(feature = "modern_sqlite")] Recursive, } @@ -285,7 +284,6 @@ impl<'c> AuthAction<'c> { operation: TransactionOperation::from_str(operation_str), savepoint_name, }, - #[cfg(feature = "modern_sqlite")] // 3.8.3 (ffi::SQLITE_RECURSIVE, ..) => Self::Recursive, (code, arg1, arg2) => Self::Unknown { code, arg1, arg2 }, } @@ -428,11 +426,7 @@ impl InnerConnection { let boxed_hook: *mut F = p_arg.cast::<F>(); (*boxed_hook)() }); - if let Ok(true) = r { - 1 - } else { - 0 - } + c_int::from(r.unwrap_or_default()) } // unlike `sqlite3_create_function_v2`, we cannot specify a `xDestroy` with @@ -570,11 +564,7 @@ impl InnerConnection { let boxed_handler: *mut F = p_arg.cast::<F>(); (*boxed_handler)() }); - if let Ok(true) = r { - 1 - } else { - 0 - } + c_int::from(r.unwrap_or_default()) } if let Some(handler) = handler { diff --git a/src/inner_connection.rs b/src/inner_connection.rs index e5bc3f1..275e846 100644 --- a/src/inner_connection.rs +++ b/src/inner_connection.rs @@ -69,13 +69,13 @@ impl InnerConnection { // Replicate the check for sane open flags from SQLite, because the check in // SQLite itself wasn't added until version 3.7.3. - debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02); - debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04); + debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits(), 0x02); + debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits(), 0x04); debug_assert_eq!( - 1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, + 1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits(), 0x40 ); - if (1 << (flags.bits & 0x7)) & 0x46 == 0 { + if (1 << (flags.bits() & 0x7)) & 0x46 == 0 { return Err(Error::SqliteFailure( ffi::Error::new(ffi::SQLITE_MISUSE), None, @@ -105,7 +105,7 @@ impl InnerConnection { { e = Error::SqliteFailure( ffi::Error::new(r), - Some(format!("{}: {}", msg, c_path.to_string_lossy())), + Some(format!("{msg}: {}", c_path.to_string_lossy())), ); } ffi::sqlite3_close(db); @@ -295,7 +295,6 @@ impl InnerConnection { unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 } } - #[cfg(feature = "modern_sqlite")] // 3.8.6 pub fn is_busy(&self) -> bool { let db = self.db(); unsafe { @@ -310,7 +309,6 @@ impl InnerConnection { false } - #[cfg(feature = "modern_sqlite")] // 3.10.0 pub fn cache_flush(&mut self) -> Result<()> { crate::error::check(unsafe { ffi::sqlite3_db_cacheflush(self.db()) }) } @@ -319,7 +317,6 @@ impl InnerConnection { #[inline] fn remove_hooks(&mut self) {} - #[cfg(feature = "modern_sqlite")] // 3.7.11 pub fn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool> { let name = db_name.as_cstring()?; let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) }; @@ -328,7 +325,7 @@ impl InnerConnection { 1 => Ok(true), -1 => Err(Error::SqliteFailure( ffi::Error::new(ffi::SQLITE_MISUSE), - Some(format!("{:?} is not the name of a database", db_name)), + Some(format!("{db_name:?} is not the name of a database")), )), _ => Err(error_from_sqlite_code( r, @@ -354,7 +351,7 @@ impl InnerConnection { 2 => Ok(super::transaction::TransactionState::Write), -1 => Err(Error::SqliteFailure( ffi::Error::new(ffi::SQLITE_MISUSE), - Some(format!("{:?} is not the name of a valid schema", db_name)), + Some(format!("{db_name:?} is not the name of a valid schema")), )), _ => Err(error_from_sqlite_code( r, @@ -374,15 +371,7 @@ impl Drop for InnerConnection { #[allow(unused_must_use)] #[inline] fn drop(&mut self) { - use std::thread::panicking; - - if let Err(e) = self.close() { - if panicking() { - eprintln!("Error while closing SQLite connection: {:?}", e); - } else { - panic!("Error while closing SQLite connection: {:?}", e); - } - } + self.close(); } } @@ -62,7 +62,7 @@ use std::ffi::{CStr, CString}; use std::fmt; use std::os::raw::{c_char, c_int}; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::result; use std::str; use std::sync::atomic::Ordering; @@ -140,13 +140,6 @@ pub(crate) use util::SmallCString; // Number of cached prepared statements we'll hold on to. const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16; -/// To be used when your statement has no [parameter][sqlite-varparam]. -/// -/// [sqlite-varparam]: https://sqlite.org/lang_expr.html#varparam -/// -/// This is deprecated in favor of using an empty array literal. -#[deprecated = "Use an empty array instead; `stmt.execute(NO_PARAMS)` => `stmt.execute([])`"] -pub const NO_PARAMS: &[&dyn ToSql] = &[]; /// A macro making it more convenient to longer lists of /// parameters as a `&[&dyn ToSql]`. @@ -314,12 +307,6 @@ pub const TEMP_DB: DatabaseName<'static> = DatabaseName::Temp; // Currently DatabaseName is only used by the backup and blob mods, so hide // this (private) impl to avoid dead code warnings. -#[cfg(any( - feature = "backup", - feature = "blob", - feature = "session", - feature = "modern_sqlite" -))] impl DatabaseName<'_> { #[inline] fn as_cstring(&self) -> Result<SmallCString> { @@ -336,7 +323,6 @@ impl DatabaseName<'_> { pub struct Connection { db: RefCell<InnerConnection>, cache: StatementCache, - path: Option<PathBuf>, } unsafe impl Send for Connection {} @@ -433,7 +419,6 @@ impl Connection { InnerConnection::open_with_flags(&c_path, flags, None).map(|db| Connection { db: RefCell::new(db), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), - path: Some(path.as_ref().to_path_buf()), }) } @@ -458,7 +443,6 @@ impl Connection { InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs)).map(|db| Connection { db: RefCell::new(db), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), - path: Some(path.as_ref().to_path_buf()), }) } @@ -540,7 +524,7 @@ impl Connection { /// ```rust,no_run /// # use rusqlite::{Connection}; /// fn update_rows(conn: &Connection) { - /// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", [1i32]) { + /// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?1", [1i32]) { /// Ok(updated) => println!("{} rows were updated", updated), /// Err(err) => println!("update failed: {}", err), /// } @@ -586,12 +570,23 @@ impl Connection { /// Returns the path to the database file, if one exists and is known. /// + /// Returns `Some("")` for a temporary or in-memory database. + /// /// Note that in some cases [PRAGMA /// database_list](https://sqlite.org/pragma.html#pragma_database_list) is /// likely to be more robust. #[inline] - pub fn path(&self) -> Option<&Path> { - self.path.as_deref() + pub fn path(&self) -> Option<&str> { + unsafe { + let db = self.handle(); + let db_name = DatabaseName::Main.as_cstring().unwrap(); + let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr()); + if db_filename.is_null() { + None + } else { + CStr::from_ptr(db_filename).to_str().ok() + } + } } /// Attempts to free as much heap memory as possible from the database @@ -604,26 +599,6 @@ impl Connection { self.db.borrow_mut().release_memory() } - /// Convenience method to prepare and execute a single SQL statement with - /// named parameter(s). - /// - /// On success, returns the number of rows that were changed or inserted or - /// deleted (via `sqlite3_changes`). - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[deprecated = "You can use `execute` with named params now."] - pub fn execute_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<usize> { - // This function itself is deprecated, so it's fine - #![allow(deprecated)] - self.prepare(sql).and_then(|mut stmt| { - stmt.check_no_tail() - .and_then(|_| stmt.execute_named(params)) - }) - } - /// Get the SQLite rowid of the most recent successful INSERT. /// /// Uses [sqlite3_last_insert_rowid](https://www.sqlite.org/c3ref/last_insert_rowid.html) under @@ -671,26 +646,10 @@ impl Connection { stmt.query_row(params, f) } - /// Convenience method to execute a query with named parameter(s) that is - /// expected to return a single row. - /// - /// If the query returns more than one row, all rows except the first are - /// ignored. - /// - /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the - /// query truly is optional, you can call `.optional()` on the result of - /// this to get a `Result<Option<T>>`. - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[deprecated = "You can use `query_row` with named params now."] - pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &dyn ToSql)], f: F) -> Result<T> - where - F: FnOnce(&Row<'_>) -> Result<T>, - { - self.query_row(sql, params, f) + // https://sqlite.org/tclsqlite.html#onecolumn + #[cfg(test)] + pub(crate) fn one_column<T: crate::types::FromSql>(&self, sql: &str) -> Result<T> { + self.query_row(sql, [], |r| r.get(0)) } /// Convenience method to execute a query that is expected to return a @@ -739,7 +698,7 @@ impl Connection { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn insert_new_people(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?)")?; + /// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?1)")?; /// stmt.execute(["Joe Smith"])?; /// stmt.execute(["Bob Jones"])?; /// Ok(()) @@ -921,12 +880,30 @@ impl Connection { /// This function is unsafe because improper use may impact the Connection. #[inline] pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result<Connection> { - let db_path = db_filename(db); let db = InnerConnection::new(db, false); Ok(Connection { db: RefCell::new(db), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), - path: db_path, + }) + } + + /// Create a `Connection` from a raw owned handle. + /// + /// The returned connection will attempt to close the inner connection + /// when dropped/closed. This function should only be called on connections + /// owned by the caller. + /// + /// # Safety + /// + /// This function is unsafe because improper use may impact the Connection. + /// In particular, it should only be called on connections created + /// and owned by the caller, e.g. as a result of calling ffi::sqlite3_open(). + #[inline] + pub unsafe fn from_handle_owned(db: *mut ffi::sqlite3) -> Result<Connection> { + let db = InnerConnection::new(db, true); + Ok(Connection { + db: RefCell::new(db), + cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), }) } @@ -961,22 +938,16 @@ impl Connection { /// Determine if all associated prepared statements have been reset. #[inline] - #[cfg(feature = "modern_sqlite")] // 3.8.6 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] pub fn is_busy(&self) -> bool { self.db.borrow().is_busy() } /// Flush caches to disk mid-transaction - #[cfg(feature = "modern_sqlite")] // 3.10.0 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] pub fn cache_flush(&self) -> Result<()> { self.db.borrow_mut().cache_flush() } /// Determine if a database is read-only - #[cfg(feature = "modern_sqlite")] // 3.7.11 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] pub fn is_readonly(&self, db_name: DatabaseName<'_>) -> Result<bool> { self.db.borrow().db_readonly(db_name) } @@ -985,7 +956,7 @@ impl Connection { impl fmt::Debug for Connection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Connection") - .field("path", &self.path) + .field("path", &self.path()) .finish() } } @@ -1058,6 +1029,7 @@ bitflags::bitflags! { /// The default open flags are `SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE /// | SQLITE_OPEN_URI | SQLITE_OPEN_NO_MUTEX`. See [`Connection::open`] for /// some discussion about these flags. + #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(C)] pub struct OpenFlags: ::std::os::raw::c_int { /// The database is opened in read-only mode. @@ -1070,9 +1042,9 @@ bitflags::bitflags! { /// The database is created if it does not already exist const SQLITE_OPEN_CREATE = ffi::SQLITE_OPEN_CREATE; /// The filename can be interpreted as a URI if this flag is set. - const SQLITE_OPEN_URI = 0x0000_0040; + const SQLITE_OPEN_URI = ffi::SQLITE_OPEN_URI; /// The database will be opened as an in-memory database. - const SQLITE_OPEN_MEMORY = 0x0000_0080; + const SQLITE_OPEN_MEMORY = ffi::SQLITE_OPEN_MEMORY; /// The new database connection will not use a per-connection mutex (the /// connection will use the "multi-thread" threading mode, in SQLite /// parlance). @@ -1176,21 +1148,6 @@ impl InterruptHandle { } } -#[cfg(feature = "modern_sqlite")] // 3.7.10 -unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option<PathBuf> { - let db_name = DatabaseName::Main.as_cstring().unwrap(); - let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr()); - if db_filename.is_null() { - None - } else { - CStr::from_ptr(db_filename).to_str().ok().map(PathBuf::from) - } -} -#[cfg(not(feature = "modern_sqlite"))] -unsafe fn db_filename(_: *mut ffi::sqlite3) -> Option<PathBuf> { - None -} - #[cfg(doctest)] doc_comment::doctest!("../README.md"); @@ -1277,26 +1234,40 @@ mod test { } let path_string = path.to_str().unwrap(); - let db = Connection::open(&path_string)?; - let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0)); + let db = Connection::open(path_string)?; + let the_answer: i64 = db.one_column("SELECT x FROM foo")?; - assert_eq!(42i64, the_answer?); + assert_eq!(42i64, the_answer); Ok(()) } #[test] fn test_open() { - assert!(Connection::open_in_memory().is_ok()); + Connection::open_in_memory().unwrap(); let db = checked_memory_handle(); - assert!(db.close().is_ok()); + db.close().unwrap(); + } + + #[test] + fn test_path() -> Result<()> { + let tmp = tempfile::tempdir().unwrap(); + let db = Connection::open("")?; + assert_eq!(Some(""), db.path()); + let db = Connection::open_in_memory()?; + assert_eq!(Some(""), db.path()); + let db = Connection::open("file:dummy.db?mode=memory&cache=shared")?; + assert_eq!(Some(""), db.path()); + let path = tmp.path().join("file.db"); + let db = Connection::open(path)?; + assert!(db.path().map(|p| p.ends_with("file.db")).unwrap_or(false)); + Ok(()) } #[test] fn test_open_failure() { let filename = "no_such_file.db"; let result = Connection::open_with_flags(filename, OpenFlags::SQLITE_OPEN_READ_ONLY); - assert!(result.is_err()); let err = result.unwrap_err(); if let Error::SqliteFailure(e, Some(msg)) = err { assert_eq!(ErrorCode::CannotOpen, e.code); @@ -1336,9 +1307,9 @@ mod test { } let db = Connection::open(&db_path)?; - let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0)); + let the_answer: i64 = db.one_column("SELECT x FROM foo")?; - assert_eq!(42i64, the_answer?); + assert_eq!(42i64, the_answer); Ok(()) } @@ -1391,7 +1362,7 @@ mod test { OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE, OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE, ] { - assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err()); + Connection::open_in_memory_with_flags(*bad_flags).unwrap_err(); } } @@ -1409,7 +1380,7 @@ mod test { db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")?; - assert!(db.execute_batch("INVALID SQL").is_err()); + db.execute_batch("INVALID SQL").unwrap_err(); Ok(()) } @@ -1418,13 +1389,10 @@ mod test { let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(x INTEGER)")?; - assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [1i32])?); - assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [2i32])?); + assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?1)", [1i32])?); + assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?1)", [2i32])?); - assert_eq!( - 3i32, - db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))? - ); + assert_eq!(3i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?); Ok(()) } @@ -1432,7 +1400,7 @@ mod test { #[cfg(feature = "extra_check")] fn test_execute_select() { let db = checked_memory_handle(); - let err = db.execute("SELECT 1 WHERE 1 < ?", [1i32]).unwrap_err(); + let err = db.execute("SELECT 1 WHERE 1 < ?1", [1i32]).unwrap_err(); assert_eq!( err, Error::ExecuteReturnedResults, @@ -1477,7 +1445,7 @@ mod test { let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(x INTEGER);")?; - let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?; + let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?1)")?; assert_eq!(insert_stmt.execute([1i32])?, 1); assert_eq!(insert_stmt.execute([2i32])?, 1); assert_eq!(insert_stmt.execute([3i32])?, 1); @@ -1486,7 +1454,7 @@ mod test { assert_eq!(insert_stmt.execute(["goodbye"])?, 1); assert_eq!(insert_stmt.execute([types::Null])?, 1); - let mut update_stmt = db.prepare("UPDATE foo SET x=? WHERE x<?")?; + let mut update_stmt = db.prepare("UPDATE foo SET x=?1 WHERE x<?2")?; assert_eq!(update_stmt.execute([3i32, 3i32])?, 2); assert_eq!(update_stmt.execute([3i32, 3i32])?, 0); assert_eq!(update_stmt.execute([8i32, 8i32])?, 3); @@ -1498,12 +1466,12 @@ mod test { let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(x INTEGER);")?; - let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?; + let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?1)")?; assert_eq!(insert_stmt.execute([1i32])?, 1); assert_eq!(insert_stmt.execute([2i32])?, 1); assert_eq!(insert_stmt.execute([3i32])?, 1); - let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")?; + let mut query = db.prepare("SELECT x FROM foo WHERE x < ?1 ORDER BY x DESC")?; { let mut rows = query.query([4i32])?; let mut v = Vec::<i32>::new(); @@ -1559,12 +1527,9 @@ mod test { END;"; db.execute_batch(sql)?; - assert_eq!( - 10i64, - db.query_row::<i64, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))? - ); + assert_eq!(10i64, db.one_column::<i64>("SELECT SUM(x) FROM foo")?); - let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", [], |r| r.get(0)); + let result: Result<i64> = db.one_column("SELECT x FROM foo WHERE x > 5"); match result.unwrap_err() { Error::QueryReturnedNoRows => (), err => panic!("Unexpected error {}", err), @@ -1572,7 +1537,7 @@ mod test { let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(())); - assert!(bad_query_result.is_err()); + bad_query_result.unwrap_err(); Ok(()) } @@ -1580,34 +1545,31 @@ mod test { fn test_optional() -> Result<()> { let db = Connection::open_in_memory()?; - let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 <> 0", [], |r| r.get(0)); + let result: Result<i64> = db.one_column("SELECT 1 WHERE 0 <> 0"); let result = result.optional(); match result? { None => (), _ => panic!("Unexpected result"), } - let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 == 0", [], |r| r.get(0)); + let result: Result<i64> = db.one_column("SELECT 1 WHERE 0 == 0"); let result = result.optional(); match result? { Some(1) => (), _ => panic!("Unexpected result"), } - let bad_query_result: Result<i64> = db.query_row("NOT A PROPER QUERY", [], |r| r.get(0)); + let bad_query_result: Result<i64> = db.one_column("NOT A PROPER QUERY"); let bad_query_result = bad_query_result.optional(); - assert!(bad_query_result.is_err()); + bad_query_result.unwrap_err(); Ok(()) } #[test] fn test_pragma_query_row() -> Result<()> { let db = Connection::open_in_memory()?; - assert_eq!( - "memory", - db.query_row::<String, _, _>("PRAGMA journal_mode", [], |r| r.get(0))? - ); - let mode = db.query_row::<String, _, _>("PRAGMA journal_mode=off", [], |r| r.get(0))?; + assert_eq!("memory", db.one_column::<String>("PRAGMA journal_mode")?); + let mode = db.one_column::<String>("PRAGMA journal_mode=off")?; if cfg!(features = "bundled") { assert_eq!(mode, "off"); } else { @@ -1634,7 +1596,7 @@ mod test { db.execute_batch("CREATE TABLE foo(x INTEGER);")?; let err = db.prepare("SELECT * FROM does_not_exist").unwrap_err(); - assert!(format!("{}", err).contains("does_not_exist")); + assert!(format!("{err}").contains("does_not_exist")); Ok(()) } @@ -1665,7 +1627,6 @@ mod test { } #[test] - #[cfg(feature = "modern_sqlite")] fn test_is_busy() -> Result<()> { let db = Connection::open_in_memory()?; assert!(!db.is_busy()); @@ -1688,7 +1649,7 @@ mod test { let query = "SELECT 12345"; let stmt = db.prepare(query)?; - assert!(format!("{:?}", stmt).contains(query)); + assert!(format!("{stmt:?}").contains(query)); Ok(()) } @@ -1696,18 +1657,14 @@ mod test { fn test_notnull_constraint_error() -> Result<()> { // extended error codes for constraints were added in SQLite 3.7.16; if we're // running on our bundled version, we know the extended error code exists. - #[cfg(feature = "modern_sqlite")] fn check_extended_code(extended_code: c_int) { assert_eq!(extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL); } - #[cfg(not(feature = "modern_sqlite"))] - fn check_extended_code(_extended_code: c_int) {} let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(x NOT NULL)")?; let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", []); - assert!(result.is_err()); match result.unwrap_err() { Error::SqliteFailure(err, _) => { @@ -1726,7 +1683,7 @@ mod test { let minor = (n % 1_000_000) / 1_000; let patch = n % 1_000; - assert!(version().contains(&format!("{}.{}.{}", major, minor, patch))); + assert!(version().contains(&format!("{major}.{minor}.{patch}"))); } #[test] @@ -1779,7 +1736,7 @@ mod test { let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(i, x);")?; let vals = ["foobar", "1234", "qwerty"]; - let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?, ?)")?; + let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?1, ?2)")?; for (i, v) in vals.iter().enumerate() { let i_to_insert = i as i64; assert_eq!(insert_stmt.execute(params![i_to_insert, v])?, 1); @@ -1819,6 +1776,16 @@ mod test { Ok(()) } + #[test] + fn test_from_handle_owned() -> Result<()> { + let mut handle: *mut ffi::sqlite3 = std::ptr::null_mut(); + let r = unsafe { ffi::sqlite3_open(":memory:\0".as_ptr() as *const i8, &mut handle) }; + assert_eq!(r, ffi::SQLITE_OK); + let db = unsafe { Connection::from_handle_owned(handle) }?; + db.execute_batch("PRAGMA VACUUM")?; + Ok(()) + } + mod query_and_then_tests { use super::*; @@ -1833,7 +1800,7 @@ mod test { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { CustomError::SomeError => write!(f, "my custom error"), - CustomError::Sqlite(ref se) => write!(f, "my custom error: {}", se), + CustomError::Sqlite(ref se) => write!(f, "my custom error: {se}"), } } } @@ -2045,7 +2012,7 @@ mod test { let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(x INTEGER);")?; let b: Box<dyn ToSql> = Box::new(5); - db.execute("INSERT INTO foo VALUES(?)", [b])?; + db.execute("INSERT INTO foo VALUES(?1)", [b])?; db.query_row("SELECT x FROM foo", [], |r| { assert_eq!(5, r.get_unwrap::<_, i32>(0)); Ok(()) @@ -2057,10 +2024,10 @@ mod test { let db = Connection::open_in_memory()?; db.query_row( "SELECT - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ?, ?;", + ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, + ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, + ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, + ?31, ?32, ?33, ?34;", params![ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -2102,23 +2069,18 @@ mod test { fn test_returning() -> Result<()> { let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?; - let row_id = - db.query_row::<i64, _, _>("INSERT INTO foo DEFAULT VALUES RETURNING ROWID", [], |r| { - r.get(0) - })?; + let row_id = db.one_column::<i64>("INSERT INTO foo DEFAULT VALUES RETURNING ROWID")?; assert_eq!(row_id, 1); Ok(()) } #[test] - #[cfg(feature = "modern_sqlite")] fn test_cache_flush() -> Result<()> { let db = Connection::open_in_memory()?; db.cache_flush() } #[test] - #[cfg(feature = "modern_sqlite")] pub fn db_readonly() -> Result<()> { let db = Connection::open_in_memory()?; assert!(!db.is_readonly(MAIN_DB)?); diff --git a/src/limits.rs b/src/limits.rs index 93e0bb0..d0694e3 100644 --- a/src/limits.rs +++ b/src/limits.rs @@ -39,10 +39,10 @@ pub enum Limit { /// The maximum index number of any parameter in an SQL statement. SQLITE_LIMIT_VARIABLE_NUMBER = ffi::SQLITE_LIMIT_VARIABLE_NUMBER, /// The maximum depth of recursion for triggers. - SQLITE_LIMIT_TRIGGER_DEPTH = 10, + SQLITE_LIMIT_TRIGGER_DEPTH = ffi::SQLITE_LIMIT_TRIGGER_DEPTH, /// The maximum number of auxiliary worker threads that a single prepared /// statement may start. - SQLITE_LIMIT_WORKER_THREADS = 11, + SQLITE_LIMIT_WORKER_THREADS = ffi::SQLITE_LIMIT_WORKER_THREADS, } impl Connection { @@ -71,55 +71,49 @@ mod test { #[test] fn test_limit_values() { - assert_eq!( - Limit::SQLITE_LIMIT_LENGTH as i32, - ffi::SQLITE_LIMIT_LENGTH as i32, - ); + assert_eq!(Limit::SQLITE_LIMIT_LENGTH as i32, ffi::SQLITE_LIMIT_LENGTH,); assert_eq!( Limit::SQLITE_LIMIT_SQL_LENGTH as i32, - ffi::SQLITE_LIMIT_SQL_LENGTH as i32, - ); - assert_eq!( - Limit::SQLITE_LIMIT_COLUMN as i32, - ffi::SQLITE_LIMIT_COLUMN as i32, + ffi::SQLITE_LIMIT_SQL_LENGTH, ); + assert_eq!(Limit::SQLITE_LIMIT_COLUMN as i32, ffi::SQLITE_LIMIT_COLUMN,); assert_eq!( Limit::SQLITE_LIMIT_EXPR_DEPTH as i32, - ffi::SQLITE_LIMIT_EXPR_DEPTH as i32, + ffi::SQLITE_LIMIT_EXPR_DEPTH, ); assert_eq!( Limit::SQLITE_LIMIT_COMPOUND_SELECT as i32, - ffi::SQLITE_LIMIT_COMPOUND_SELECT as i32, + ffi::SQLITE_LIMIT_COMPOUND_SELECT, ); assert_eq!( Limit::SQLITE_LIMIT_VDBE_OP as i32, - ffi::SQLITE_LIMIT_VDBE_OP as i32, + ffi::SQLITE_LIMIT_VDBE_OP, ); assert_eq!( Limit::SQLITE_LIMIT_FUNCTION_ARG as i32, - ffi::SQLITE_LIMIT_FUNCTION_ARG as i32, + ffi::SQLITE_LIMIT_FUNCTION_ARG, ); assert_eq!( Limit::SQLITE_LIMIT_ATTACHED as i32, - ffi::SQLITE_LIMIT_ATTACHED as i32, + ffi::SQLITE_LIMIT_ATTACHED, ); assert_eq!( Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH as i32, - ffi::SQLITE_LIMIT_LIKE_PATTERN_LENGTH as i32, + ffi::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, ); assert_eq!( Limit::SQLITE_LIMIT_VARIABLE_NUMBER as i32, - ffi::SQLITE_LIMIT_VARIABLE_NUMBER as i32, + ffi::SQLITE_LIMIT_VARIABLE_NUMBER, ); #[cfg(feature = "bundled")] assert_eq!( Limit::SQLITE_LIMIT_TRIGGER_DEPTH as i32, - ffi::SQLITE_LIMIT_TRIGGER_DEPTH as i32, + ffi::SQLITE_LIMIT_TRIGGER_DEPTH, ); #[cfg(feature = "bundled")] assert_eq!( Limit::SQLITE_LIMIT_WORKER_THREADS as i32, - ffi::SQLITE_LIMIT_WORKER_THREADS as i32, + ffi::SQLITE_LIMIT_WORKER_THREADS, ); } diff --git a/src/params.rs b/src/params.rs index 6ab6b5f..a4c5066 100644 --- a/src/params.rs +++ b/src/params.rs @@ -70,7 +70,7 @@ use sealed::Sealed; /// ```rust,no_run /// # use rusqlite::{Connection, Result, params}; /// fn update_rows(conn: &Connection) -> Result<()> { -/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?, ?)")?; +/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?1, ?2)")?; /// /// // Using a tuple: /// stmt.execute((0, "foobar"))?; @@ -138,9 +138,7 @@ use sealed::Sealed; /// ## No parameters /// /// You can just use an empty tuple or the empty array literal to run a query -/// that accepts no parameters. (The `rusqlite::NO_PARAMS` constant which was -/// common in previous versions of this library is no longer needed, and is now -/// deprecated). +/// that accepts no parameters. /// /// ### Example (no parameters) /// @@ -192,8 +190,7 @@ pub trait Params: Sealed { } // Explicitly impl for empty array. Critically, for `conn.execute([])` to be -// unambiguous, this must be the *only* implementation for an empty array. This -// avoids `NO_PARAMS` being a necessary part of the API. +// unambiguous, this must be the *only* implementation for an empty array. // // This sadly prevents `impl<T: ToSql, const N: usize> Params for [T; N]`, which // forces people to use `params![...]` or `rusqlite::params_from_iter` for long @@ -357,7 +354,7 @@ impl_for_array_ref!( /// fn query(conn: &Connection, ids: &BTreeSet<String>) -> Result<()> { /// assert_eq!(ids.len(), 3, "Unrealistic sample code"); /// -/// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?, ?, ?)")?; +/// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?1, ?2, ?3)")?; /// let _rows = stmt.query(params_from_iter(ids.iter()))?; /// /// // use _rows... diff --git a/src/pragma.rs b/src/pragma.rs index 673478a..338be5f 100644 --- a/src/pragma.rs +++ b/src/pragma.rs @@ -37,7 +37,7 @@ impl Sql { } else { Err(Error::SqliteFailure( ffi::Error::new(ffi::SQLITE_MISUSE), - Some(format!("Invalid keyword \"{}\"", keyword)), + Some(format!("Invalid keyword \"{keyword}\"")), )) } } @@ -67,14 +67,14 @@ impl Sql { ToSqlOutput::ZeroBlob(_) => { return Err(Error::SqliteFailure( ffi::Error::new(ffi::SQLITE_MISUSE), - Some(format!("Unsupported value \"{:?}\"", value)), + Some(format!("Unsupported value \"{value:?}\"")), )); } #[cfg(feature = "array")] ToSqlOutput::Array(_) => { return Err(Error::SqliteFailure( ffi::Error::new(ffi::SQLITE_MISUSE), - Some(format!("Unsupported value \"{:?}\"", value)), + Some(format!("Unsupported value \"{value:?}\"")), )); } }; @@ -92,7 +92,7 @@ impl Sql { _ => { return Err(Error::SqliteFailure( ffi::Error::new(ffi::SQLITE_MISUSE), - Some(format!("Unsupported value \"{:?}\"", value)), + Some(format!("Unsupported value \"{value:?}\"")), )); } }; @@ -211,7 +211,7 @@ impl Connection { /// (e.g. `integrity_check`). /// /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20: - /// `SELECT * FROM pragma_table_info(?);` + /// `SELECT * FROM pragma_table_info(?1);` pub fn pragma<F, V>( &self, schema_name: Option<DatabaseName<'_>>, @@ -303,15 +303,15 @@ fn is_identifier(s: &str) -> bool { } fn is_identifier_start(c: char) -> bool { - ('A'..='Z').contains(&c) || c == '_' || ('a'..='z').contains(&c) || c > '\x7F' + c.is_ascii_uppercase() || c == '_' || c.is_ascii_lowercase() || c > '\x7F' } fn is_identifier_continue(c: char) -> bool { c == '$' - || ('0'..='9').contains(&c) - || ('A'..='Z').contains(&c) + || c.is_ascii_digit() + || c.is_ascii_uppercase() || c == '_' - || ('a'..='z').contains(&c) + || c.is_ascii_lowercase() || c > '\x7F' } @@ -333,10 +333,7 @@ mod test { #[cfg(feature = "modern_sqlite")] fn pragma_func_query_value() -> Result<()> { let db = Connection::open_in_memory()?; - let user_version: i32 = - db.query_row("SELECT user_version FROM pragma_user_version", [], |row| { - row.get(0) - })?; + let user_version: i32 = db.one_column("SELECT user_version FROM pragma_user_version")?; assert_eq!(0, user_version); Ok(()) } @@ -369,7 +366,7 @@ mod test { fn pragma() -> Result<()> { let db = Connection::open_in_memory()?; let mut columns = Vec::new(); - db.pragma(None, "table_info", &"sqlite_master", |row| { + db.pragma(None, "table_info", "sqlite_master", |row| { let column: String = row.get(1)?; columns.push(column); Ok(()) @@ -382,7 +379,7 @@ mod test { #[cfg(feature = "modern_sqlite")] fn pragma_func() -> Result<()> { let db = Connection::open_in_memory()?; - let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?)")?; + let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?1)")?; let mut columns = Vec::new(); let mut rows = table_info.query(["sqlite_master"])?; @@ -412,8 +409,8 @@ mod test { journal_mode, ); // Sanity checks to ensure the move to a generic `ToSql` wasn't breaking - let mode = db - .pragma_update_and_check(None, "journal_mode", &"OFF", |row| row.get::<_, String>(0))?; + let mode = + db.pragma_update_and_check(None, "journal_mode", "OFF", |row| row.get::<_, String>(0))?; assert!(mode == "off" || mode == "memory", "mode: {:?}", mode); let param: &dyn crate::ToSql = &"OFF"; @@ -448,7 +445,7 @@ mod test { #[test] fn locking_mode() -> Result<()> { let db = Connection::open_in_memory()?; - let r = db.pragma_update(None, "locking_mode", &"exclusive"); + let r = db.pragma_update(None, "locking_mode", "exclusive"); if cfg!(feature = "extra_check") { r.unwrap_err(); } else { diff --git a/src/raw_statement.rs b/src/raw_statement.rs index f057761..1683c7b 100644 --- a/src/raw_statement.rs +++ b/src/raw_statement.rs @@ -1,7 +1,6 @@ use super::ffi; use super::StatementStatus; use crate::util::ParamIndexCache; -#[cfg(feature = "modern_sqlite")] use crate::util::SqliteMallocString; use std::ffi::CStr; use std::os::raw::c_int; @@ -170,8 +169,10 @@ impl RawStatement { } #[inline] - pub fn clear_bindings(&self) -> c_int { - unsafe { ffi::sqlite3_clear_bindings(self.ptr) } + pub fn clear_bindings(&self) { + unsafe { + ffi::sqlite3_clear_bindings(self.ptr); + } // rc is always SQLITE_OK } #[inline] @@ -197,13 +198,11 @@ impl RawStatement { // does not work for PRAGMA #[inline] - #[cfg(all(feature = "extra_check", feature = "modern_sqlite"))] // 3.7.4 pub fn readonly(&self) -> bool { unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 } } #[inline] - #[cfg(feature = "modern_sqlite")] // 3.14.0 pub(crate) fn expanded_sql(&self) -> Option<SqliteMallocString> { unsafe { SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(self.ptr)) } } @@ -338,20 +338,6 @@ impl<'stmt> Row<'stmt> { pub fn get_ref_unwrap<I: RowIndex>(&self, idx: I) -> ValueRef<'_> { self.get_ref(idx).unwrap() } - - /// Renamed to [`get_ref`](Row::get_ref). - #[deprecated = "Use [`get_ref`](Row::get_ref) instead."] - #[inline] - pub fn get_raw_checked<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> { - self.get_ref(idx) - } - - /// Renamed to [`get_ref_unwrap`](Row::get_ref_unwrap). - #[deprecated = "Use [`get_ref_unwrap`](Row::get_ref_unwrap) instead."] - #[inline] - pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'_> { - self.get_ref_unwrap(idx) - } } impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> { @@ -360,6 +346,46 @@ impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> { } } +/// Debug `Row` like an ordered `Map<Result<&str>, Result<(Type, ValueRef)>>` +/// with column name as key except that for `Type::Blob` only its size is +/// printed (not its content). +impl<'stmt> std::fmt::Debug for Row<'stmt> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut dm = f.debug_map(); + for c in 0..self.stmt.column_count() { + let name = self.stmt.column_name(c); + dm.key(&name); + let value = self.get_ref(c); + match value { + Ok(value) => { + let dt = value.data_type(); + match value { + ValueRef::Null => { + dm.value(&(dt, ())); + } + ValueRef::Integer(i) => { + dm.value(&(dt, i)); + } + ValueRef::Real(f) => { + dm.value(&(dt, f)); + } + ValueRef::Text(s) => { + dm.value(&(dt, String::from_utf8_lossy(s))); + } + ValueRef::Blob(b) => { + dm.value(&(dt, b.len())); + } + } + } + Err(ref _err) => { + dm.value(&value); + } + } + } + dm.finish() + } +} + mod sealed { /// This trait exists just to ensure that the only impls of `trait Params` /// that are allowed are ones in this crate. @@ -391,7 +417,7 @@ impl RowIndex for usize { impl RowIndex for &'_ str { #[inline] fn idx(&self, stmt: &Statement<'_>) -> Result<usize> { - stmt.column_index(*self) + stmt.column_index(self) } } @@ -448,7 +474,7 @@ mod tests { let val = conn.query_row("SELECT a FROM test", [], |row| <(u32,)>::try_from(row))?; assert_eq!(val, (42,)); let fail = conn.query_row("SELECT a FROM test", [], |row| <(u32, u32)>::try_from(row)); - assert!(fail.is_err()); + fail.unwrap_err(); Ok(()) } @@ -466,7 +492,7 @@ mod tests { let fail = conn.query_row("SELECT a, b FROM test", [], |row| { <(u32, u32, u32)>::try_from(row) }); - assert!(fail.is_err()); + fail.unwrap_err(); Ok(()) } diff --git a/src/session.rs b/src/session.rs index f8aa764..c42bbd6 100644 --- a/src/session.rs +++ b/src/session.rs @@ -19,12 +19,14 @@ use crate::{errmsg_to_string, str_to_cstring, Connection, DatabaseName, Result}; // https://sqlite.org/session.html +type Filter = Option<Box<dyn Fn(&str) -> bool>>; + /// An instance of this object is a session that can be /// used to record changes to a database. pub struct Session<'conn> { phantom: PhantomData<&'conn Connection>, s: *mut ffi::sqlite3_session, - filter: Option<Box<dyn Fn(&str) -> bool>>, + filter: Filter, } impl Session<'_> { @@ -73,13 +75,10 @@ impl Session<'_> { let c_slice = CStr::from_ptr(tbl_str).to_bytes(); str::from_utf8(c_slice) }; - if let Ok(true) = + c_int::from( catch_unwind(|| (*boxed_filter)(tbl_name.expect("non-utf8 table name"))) - { - 1 - } else { - 0 - } + .unwrap_or_default(), + ) } match filter { @@ -191,7 +190,7 @@ impl Session<'_> { #[inline] pub fn set_enabled(&mut self, enabled: bool) { unsafe { - ffi::sqlite3session_enable(self.s, if enabled { 1 } else { 0 }); + ffi::sqlite3session_enable(self.s, c_int::from(enabled)); } } @@ -205,7 +204,7 @@ impl Session<'_> { #[inline] pub fn set_indirect(&mut self, indirect: bool) { unsafe { - ffi::sqlite3session_indirect(self.s, if indirect { 1 } else { 0 }); + ffi::sqlite3session_indirect(self.s, c_int::from(indirect)); } } } @@ -656,7 +655,7 @@ impl Connection { /// See [here](https://sqlite.org/session.html#SQLITE_CHANGESET_CONFLICT) for details. #[allow(missing_docs)] #[repr(i32)] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[non_exhaustive] #[allow(clippy::upper_case_acronyms)] pub enum ConflictType { @@ -684,7 +683,7 @@ impl From<i32> for ConflictType { /// See [here](https://sqlite.org/session.html#SQLITE_CHANGESET_ABORT) for details. #[allow(missing_docs)] #[repr(i32)] -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[non_exhaustive] #[allow(clippy::upper_case_acronyms)] pub enum ConflictAction { @@ -706,13 +705,9 @@ where str::from_utf8(c_slice) }; match *tuple { - (Some(ref filter), _) => { - if let Ok(true) = catch_unwind(|| filter(tbl_name.expect("illegal table name"))) { - 1 - } else { - 0 - } - } + (Some(ref filter), _) => c_int::from( + catch_unwind(|| filter(tbl_name.expect("illegal table name"))).unwrap_or_default(), + ), _ => unimplemented!(), } } @@ -784,7 +779,7 @@ mod test { assert!(session.is_empty()); session.attach(None)?; - db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?; + db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?; session.changeset() } @@ -797,7 +792,7 @@ mod test { assert!(session.is_empty()); session.attach(None)?; - db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?; + db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?; let mut output = Vec::new(); session.changeset_strm(&mut output)?; @@ -857,7 +852,7 @@ mod test { )?; assert!(!CALLED.load(Ordering::Relaxed)); - let check = db.query_row("SELECT 1 FROM foo WHERE t = ?", ["bar"], |row| { + let check = db.query_row("SELECT 1 FROM foo WHERE t = ?1", ["bar"], |row| { row.get::<_, i32>(0) })?; assert_eq!(1, check); @@ -892,7 +887,7 @@ mod test { |_conflict_type, _item| ConflictAction::SQLITE_CHANGESET_OMIT, )?; - let check = db.query_row("SELECT 1 FROM foo WHERE t = ?", ["bar"], |row| { + let check = db.query_row("SELECT 1 FROM foo WHERE t = ?1", ["bar"], |row| { row.get::<_, i32>(0) })?; assert_eq!(1, check); @@ -908,7 +903,7 @@ mod test { assert!(session.is_empty()); session.attach(None)?; - db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?; + db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?; assert!(!session.is_empty()); Ok(()) diff --git a/src/statement.rs b/src/statement.rs index ee5e220..982567a 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -33,7 +33,7 @@ impl Statement<'_> { /// ```rust,no_run /// # use rusqlite::{Connection, Result, params}; /// fn update_rows(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("UPDATE foo SET bar = ? WHERE qux = ?")?; + /// let mut stmt = conn.prepare("UPDATE foo SET bar = ?1 WHERE qux = ?2")?; /// // For a single parameter, or a parameter where all the values have /// // the same type, just passing an array is simplest. /// stmt.execute([2i32])?; @@ -58,7 +58,7 @@ impl Statement<'_> { /// fn store_file(conn: &Connection, path: &str, data: &[u8]) -> Result<()> { /// # // no need to do it for real. /// # fn sha256(_: &[u8]) -> [u8; 32] { [0; 32] } - /// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?, ?, ?)"; + /// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?1, ?2, ?3)"; /// let mut stmt = conn.prepare_cached(query)?; /// let hash: [u8; 32] = sha256(data); /// // The easiest way to pass positional parameters of have several @@ -114,31 +114,6 @@ impl Statement<'_> { self.execute_with_bound_parameters() } - /// Execute the prepared statement with named parameter(s). - /// - /// Note: This function is deprecated in favor of [`Statement::execute`], - /// which can now take named parameters directly. - /// - /// If any parameters that were in the prepared statement are not included - /// in `params`, they will continue to use the most-recently bound value - /// from a previous call to `execute_named`, or `NULL` if they have never - /// been bound. - /// - /// On success, returns the number of rows that were changed or inserted or - /// deleted (via `sqlite3_changes`). - /// - /// # Failure - /// - /// Will return `Err` if binding parameters fails, the executed statement - /// returns rows (in which case `query` should be used instead), or the - /// underlying SQLite call fails. - #[doc(hidden)] - #[deprecated = "You can use `execute` with named params now."] - #[inline] - pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<usize> { - self.execute(params) - } - /// Execute an INSERT and return the ROWID. /// /// # Note @@ -193,7 +168,7 @@ impl Statement<'_> { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn query(conn: &Connection, name: &str) -> Result<()> { - /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?; + /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?1")?; /// let mut rows = stmt.query(rusqlite::params![name])?; /// while let Some(row) = rows.next()? { /// // ... @@ -202,12 +177,12 @@ impl Statement<'_> { /// } /// ``` /// - /// Or, equivalently (but without the [`params!`] macro). + /// Or, equivalently (but without the [`crate::params!`] macro). /// /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn query(conn: &Connection, name: &str) -> Result<()> { - /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?; + /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?1")?; /// let mut rows = stmt.query([name])?; /// while let Some(row) = rows.next()? { /// // ... @@ -254,26 +229,6 @@ impl Statement<'_> { Ok(Rows::new(self)) } - /// Execute the prepared statement with named parameter(s), returning a - /// handle for the resulting rows. - /// - /// Note: This function is deprecated in favor of [`Statement::query`], - /// which can now take named parameters directly. - /// - /// If any parameters that were in the prepared statement are not included - /// in `params`, they will continue to use the most-recently bound value - /// from a previous call to `query_named`, or `NULL` if they have never been - /// bound. - /// - /// # Failure - /// - /// Will return `Err` if binding parameters fails. - #[doc(hidden)] - #[deprecated = "You can use `query` with named params now."] - pub fn query_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<Rows<'_>> { - self.query(params) - } - /// Executes the prepared statement and maps a function over the resulting /// rows, returning an iterator over the mapped function results. /// @@ -328,37 +283,6 @@ impl Statement<'_> { self.query(params).map(|rows| rows.mapped(f)) } - /// Execute the prepared statement with named parameter(s), returning an - /// iterator over the result of calling the mapping function over the - /// query's rows. - /// - /// Note: This function is deprecated in favor of [`Statement::query_map`], - /// which can now take named parameters directly. - /// - /// If any parameters that were in the prepared statement - /// are not included in `params`, they will continue to use the - /// most-recently bound value from a previous call to `query_named`, - /// or `NULL` if they have never been bound. - /// - /// `f` is used to transform the _streaming_ iterator into a _standard_ - /// iterator. - /// - /// ## Failure - /// - /// Will return `Err` if binding parameters fails. - #[doc(hidden)] - #[deprecated = "You can use `query_map` with named params now."] - pub fn query_map_named<T, F>( - &mut self, - params: &[(&str, &dyn ToSql)], - f: F, - ) -> Result<MappedRows<'_, F>> - where - F: FnMut(&Row<'_>) -> Result<T>, - { - self.query_map(params, f) - } - /// Executes the prepared statement and maps a function over the resulting /// rows, where the function returns a `Result` with `Error` type /// implementing `std::convert::From<Error>` (so errors can be unified). @@ -398,7 +322,7 @@ impl Statement<'_> { /// ```rust,no_run /// # use rusqlite::{Connection, Result}; /// fn get_names(conn: &Connection) -> Result<Vec<String>> { - /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?")?; + /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?1")?; /// let rows = stmt.query_and_then(["one"], |row| row.get::<_, String>(0))?; /// /// let mut persons = Vec::new(); @@ -423,36 +347,6 @@ impl Statement<'_> { self.query(params).map(|rows| rows.and_then(f)) } - /// Execute the prepared statement with named parameter(s), returning an - /// iterator over the result of calling the mapping function over the - /// query's rows. - /// - /// Note: This function is deprecated in favor of - /// [`Statement::query_and_then`], which can now take named parameters - /// directly. - /// - /// If any parameters that were in the prepared statement are not included - /// in `params`, they will continue to use the most-recently bound value - /// from a previous call to `query_named`, or `NULL` if they have never been - /// bound. - /// - /// ## Failure - /// - /// Will return `Err` if binding parameters fails. - #[doc(hidden)] - #[deprecated = "You can use `query_and_then` with named params now."] - pub fn query_and_then_named<T, E, F>( - &mut self, - params: &[(&str, &dyn ToSql)], - f: F, - ) -> Result<AndThenRows<'_, F>> - where - E: From<Error>, - F: FnMut(&Row<'_>) -> Result<T, E>, - { - self.query_and_then(params, f) - } - /// Return `true` if a query in the SQL statement it executes returns one /// or more rows and `false` if the SQL returns an empty set. #[inline] @@ -487,35 +381,6 @@ impl Statement<'_> { rows.get_expected_row().and_then(f) } - /// Convenience method to execute a query with named parameter(s) that is - /// expected to return a single row. - /// - /// Note: This function is deprecated in favor of - /// [`Statement::query_and_then`], which can now take named parameters - /// directly. - /// - /// If the query returns more than one row, all rows except the first are - /// ignored. - /// - /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the - /// query truly is optional, you can call - /// [`.optional()`](crate::OptionalExtension::optional) on the result of - /// this to get a `Result<Option<T>>` (requires that the trait - /// `rusqlite::OptionalExtension` is imported). - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[doc(hidden)] - #[deprecated = "You can use `query_row` with named params now."] - pub fn query_row_named<T, F>(&mut self, params: &[(&str, &dyn ToSql)], f: F) -> Result<T> - where - F: FnOnce(&Row<'_>) -> Result<T>, - { - self.query_row(params, f) - } - /// Consumes the statement. /// /// Functionally equivalent to the `Drop` implementation, but allows @@ -796,7 +661,7 @@ impl Statement<'_> { self.conn.decode_result(stmt.finalize()) } - #[cfg(all(feature = "modern_sqlite", feature = "extra_check"))] + #[cfg(feature = "extra_check")] #[inline] fn check_update(&self) -> Result<()> { // sqlite3_column_count works for DML but not for DDL (ie ALTER) @@ -806,16 +671,6 @@ impl Statement<'_> { Ok(()) } - #[cfg(all(not(feature = "modern_sqlite"), feature = "extra_check"))] - #[inline] - fn check_update(&self) -> Result<()> { - // sqlite3_column_count works for DML but not for DDL (ie ALTER) - if self.column_count() > 0 { - return Err(Error::ExecuteReturnedResults); - } - Ok(()) - } - #[cfg(not(feature = "extra_check"))] #[inline] #[allow(clippy::unnecessary_wraps)] @@ -825,8 +680,6 @@ impl Statement<'_> { /// Returns a string containing the SQL text of prepared statement with /// bound parameters expanded. - #[cfg(feature = "modern_sqlite")] - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] pub fn expanded_sql(&self) -> Option<String> { self.stmt .expanded_sql() @@ -856,6 +709,12 @@ impl Statement<'_> { self.stmt.is_explain() } + /// Returns true if the statement is read only. + #[inline] + pub fn readonly(&self) -> bool { + self.stmt.readonly() + } + #[cfg(feature = "extra_check")] #[inline] pub(crate) fn check_no_tail(&self) -> Result<()> { @@ -882,6 +741,11 @@ impl Statement<'_> { mem::swap(&mut stmt, &mut self.stmt); stmt } + + /// Reset all bindings + pub fn clear_bindings(&mut self) { + self.stmt.clear_bindings() + } } impl fmt::Debug for Statement<'_> { @@ -1021,13 +885,12 @@ mod test { use crate::{params_from_iter, Connection, Error, Result}; #[test] - #[allow(deprecated)] fn test_execute_named() -> Result<()> { let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(x INTEGER)")?; assert_eq!( - db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])?, + db.execute("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])?, 1 ); assert_eq!( @@ -1044,7 +907,7 @@ mod test { assert_eq!( 6i32, - db.query_row_named::<i32, _>( + db.query_row::<i32, _, _>( "SELECT SUM(x) FROM foo WHERE x > :x", &[(":x", &0i32)], |r| r.get(0) @@ -1062,7 +925,6 @@ mod test { } #[test] - #[allow(deprecated)] fn test_stmt_execute_named() -> Result<()> { let db = Connection::open_in_memory()?; let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ @@ -1070,22 +932,17 @@ mod test { db.execute_batch(sql)?; let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")?; - stmt.execute_named(&[(":name", &"one")])?; + stmt.execute(&[(":name", &"one")])?; let mut stmt = db.prepare("SELECT COUNT(*) FROM test WHERE name = :name")?; assert_eq!( 1i32, - stmt.query_row_named::<i32, _>(&[(":name", &"one")], |r| r.get(0))? - ); - assert_eq!( - 1i32, stmt.query_row::<i32, _, _>(&[(":name", "one")], |r| r.get(0))? ); Ok(()) } #[test] - #[allow(deprecated)] fn test_query_named() -> Result<()> { let db = Connection::open_in_memory()?; let sql = r#" @@ -1095,24 +952,13 @@ mod test { db.execute_batch(sql)?; let mut stmt = db.prepare("SELECT id FROM test where name = :name")?; - // legacy `_named` api - { - let mut rows = stmt.query_named(&[(":name", &"one")])?; - let id: Result<i32> = rows.next()?.unwrap().get(0); - assert_eq!(Ok(1), id); - } - - // plain api - { - let mut rows = stmt.query(&[(":name", "one")])?; - let id: Result<i32> = rows.next()?.unwrap().get(0); - assert_eq!(Ok(1), id); - } + let mut rows = stmt.query(&[(":name", "one")])?; + let id: Result<i32> = rows.next()?.unwrap().get(0); + assert_eq!(Ok(1), id); Ok(()) } #[test] - #[allow(deprecated)] fn test_query_map_named() -> Result<()> { let db = Connection::open_in_memory()?; let sql = r#" @@ -1122,61 +968,13 @@ mod test { db.execute_batch(sql)?; let mut stmt = db.prepare("SELECT id FROM test where name = :name")?; - // legacy `_named` api - { - let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| { - let id: Result<i32> = row.get(0); - id.map(|i| 2 * i) - })?; - - let doubled_id: i32 = rows.next().unwrap()?; - assert_eq!(2, doubled_id); - } - // plain api - { - let mut rows = stmt.query_map(&[(":name", "one")], |row| { - let id: Result<i32> = row.get(0); - id.map(|i| 2 * i) - })?; - - let doubled_id: i32 = rows.next().unwrap()?; - assert_eq!(2, doubled_id); - } - Ok(()) - } - - #[test] - #[allow(deprecated)] - fn test_query_and_then_named() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = r#" - CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); - INSERT INTO test(id, name) VALUES (1, "one"); - INSERT INTO test(id, name) VALUES (2, "one"); - "#; - db.execute_batch(sql)?; - - let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")?; - let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| { - let id: i32 = row.get(0)?; - if id == 1 { - Ok(id) - } else { - Err(Error::SqliteSingleThreadedMode) - } + let mut rows = stmt.query_map(&[(":name", "one")], |row| { + let id: Result<i32> = row.get(0); + id.map(|i| 2 * i) })?; - // first row should be Ok let doubled_id: i32 = rows.next().unwrap()?; - assert_eq!(1, doubled_id); - - // second row should be Err - #[allow(clippy::match_wild_err_arm)] - match rows.next().unwrap() { - Ok(_) => panic!("invalid Ok"), - Err(Error::SqliteSingleThreadedMode) => (), - Err(_) => panic!("invalid Err"), - } + assert_eq!(2, doubled_id); Ok(()) } @@ -1215,17 +1013,15 @@ mod test { } #[test] - #[allow(deprecated)] fn test_unbound_parameters_are_null() -> Result<()> { let db = Connection::open_in_memory()?; let sql = "CREATE TABLE test (x TEXT, y TEXT)"; db.execute_batch(sql)?; let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")?; - stmt.execute_named(&[(":x", &"one")])?; + stmt.execute(&[(":x", &"one")])?; - let result: Option<String> = - db.query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))?; + let result: Option<String> = db.one_column("SELECT y FROM test WHERE x = 'one'")?; assert!(result.is_none()); Ok(()) } @@ -1271,8 +1067,7 @@ mod test { stmt.execute(&[(":x", "one")])?; stmt.execute(&[(":y", "two")])?; - let result: String = - db.query_row("SELECT x FROM test WHERE y = 'two'", [], |row| row.get(0))?; + let result: String = db.one_column("SELECT x FROM test WHERE y = 'two'")?; assert_eq!(result, "one"); Ok(()) } @@ -1281,7 +1076,7 @@ mod test { fn test_insert() -> Result<()> { let db = Connection::open_in_memory()?; db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?; - let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")?; + let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?1)")?; assert_eq!(stmt.insert([1i32])?, 1); assert_eq!(stmt.insert([2i32])?, 2); match stmt.insert([1i32]).unwrap_err() { @@ -1321,7 +1116,7 @@ mod test { INSERT INTO foo VALUES(2); END;"; db.execute_batch(sql)?; - let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?")?; + let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?1")?; assert!(stmt.exists([1i32])?); assert!(stmt.exists([2i32])?); assert!(!stmt.exists([0i32])?); @@ -1330,18 +1125,18 @@ mod test { #[test] fn test_tuple_params() -> Result<()> { let db = Connection::open_in_memory()?; - let s = db.query_row("SELECT printf('[%s]', ?)", ("abc",), |r| { + let s = db.query_row("SELECT printf('[%s]', ?1)", ("abc",), |r| { r.get::<_, String>(0) })?; assert_eq!(s, "[abc]"); let s = db.query_row( - "SELECT printf('%d %s %d', ?, ?, ?)", + "SELECT printf('%d %s %d', ?1, ?2, ?3)", (1i32, "abc", 2i32), |r| r.get::<_, String>(0), )?; assert_eq!(s, "1 abc 2"); let s = db.query_row( - "SELECT printf('%d %s %d %d', ?, ?, ?, ?)", + "SELECT printf('%d %s %d %d', ?1, ?2, ?3, ?4)", (1, "abc", 2i32, 4i64), |r| r.get::<_, String>(0), )?; @@ -1353,10 +1148,10 @@ mod test { ); let query = "SELECT printf( '%d %s | %d %s | %d %s | %d %s || %d %s | %d %s | %d %s | %d %s', - ?, ?, ?, ?, - ?, ?, ?, ?, - ?, ?, ?, ?, - ?, ?, ?, ? + ?1, ?2, ?3, ?4, + ?5, ?6, ?7, ?8, + ?9, ?10, ?11, ?12, + ?13, ?14, ?15, ?16 )"; let s = db.query_row(query, bigtup, |r| r.get::<_, String>(0))?; assert_eq!(s, "0 a | 1 b | 2 c | 3 d || 4 e | 5 f | 6 g | 7 h"); @@ -1372,7 +1167,7 @@ mod test { INSERT INTO foo VALUES(2, 4); END;"; db.execute_batch(sql)?; - let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?")?; + let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?1")?; let y: Result<i64> = stmt.query_row([1i32], |r| r.get(0)); assert_eq!(3i64, y?); Ok(()) @@ -1407,10 +1202,9 @@ mod test { } #[test] - #[cfg(feature = "modern_sqlite")] fn test_expanded_sql() -> Result<()> { let db = Connection::open_in_memory()?; - let stmt = db.prepare("SELECT ?")?; + let stmt = db.prepare("SELECT ?1")?; stmt.bind_parameter(&1, 1)?; assert_eq!(Some("SELECT 1".to_owned()), stmt.expanded_sql()); Ok(()) @@ -1422,7 +1216,7 @@ mod test { // dynamic slice: db.query_row( "SELECT ?1, ?2, ?3", - &[&1u8 as &dyn ToSql, &"one", &Some("one")], + [&1u8 as &dyn ToSql, &"one", &Some("one")], |row| row.get::<_, u8>(0), )?; // existing collection: @@ -1474,10 +1268,10 @@ mod test { let conn = Connection::open_in_memory()?; let mut stmt = conn.prepare("")?; assert_eq!(0, stmt.column_count()); - assert!(stmt.parameter_index("test").is_ok()); - assert!(stmt.step().is_err()); + stmt.parameter_index("test").unwrap(); + stmt.step().unwrap_err(); stmt.reset(); - assert!(stmt.execute([]).is_err()); + stmt.execute([]).unwrap_err(); Ok(()) } @@ -1507,13 +1301,13 @@ mod test { #[test] fn test_utf16_conversion() -> Result<()> { let db = Connection::open_in_memory()?; - db.pragma_update(None, "encoding", &"UTF-16le")?; + db.pragma_update(None, "encoding", "UTF-16le")?; let encoding: String = db.pragma_query_value(None, "encoding", |row| row.get(0))?; assert_eq!("UTF-16le", encoding); db.execute_batch("CREATE TABLE foo(x TEXT)")?; let expected = "テスト"; - db.execute("INSERT INTO foo(x) VALUES (?)", &[&expected])?; - let actual: String = db.query_row("SELECT x FROM foo", [], |row| row.get(0))?; + db.execute("INSERT INTO foo(x) VALUES (?1)", [&expected])?; + let actual: String = db.one_column("SELECT x FROM foo")?; assert_eq!(expected, actual); Ok(()) } @@ -1522,7 +1316,7 @@ mod test { fn test_nul_byte() -> Result<()> { let db = Connection::open_in_memory()?; let expected = "a\x00b"; - let actual: String = db.query_row("SELECT ?", [expected], |row| row.get(0))?; + let actual: String = db.query_row("SELECT ?1", [expected], |row| row.get(0))?; assert_eq!(expected, actual); Ok(()) } @@ -1537,12 +1331,19 @@ mod test { } #[test] - #[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0 + fn readonly() -> Result<()> { + let db = Connection::open_in_memory()?; + let stmt = db.prepare("SELECT 1;")?; + assert!(stmt.readonly()); + Ok(()) + } + + #[test] + #[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0 fn test_error_offset() -> Result<()> { use crate::ffi::ErrorCode; let db = Connection::open_in_memory()?; let r = db.execute_batch("SELECT CURRENT_TIMESTANP;"); - assert!(r.is_err()); match r.unwrap_err() { Error::SqlInputError { error, offset, .. } => { assert_eq!(error.code, ErrorCode::Unknown); diff --git a/src/trace.rs b/src/trace.rs index 7fc9090..ce4c80b 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -144,13 +144,13 @@ mod test { let mut db = Connection::open_in_memory()?; db.trace(Some(tracer)); { - let _ = db.query_row("SELECT ?", [1i32], |_| Ok(())); - let _ = db.query_row("SELECT ?", ["hello"], |_| Ok(())); + let _ = db.query_row("SELECT ?1", [1i32], |_| Ok(())); + let _ = db.query_row("SELECT ?1", ["hello"], |_| Ok(())); } db.trace(None); { - let _ = db.query_row("SELECT ?", [2i32], |_| Ok(())); - let _ = db.query_row("SELECT ?", ["goodbye"], |_| Ok(())); + let _ = db.query_row("SELECT ?1", [2i32], |_| Ok(())); + let _ = db.query_row("SELECT ?1", ["goodbye"], |_| Ok(())); } let traced_stmts = TRACED_STMTS.lock().unwrap(); diff --git a/src/transaction.rs b/src/transaction.rs index 2c4c6c0..5cf26a4 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -255,7 +255,7 @@ impl Savepoint<'_> { name: T, ) -> Result<Savepoint<'_>> { let name = name.into(); - conn.execute_batch(&format!("SAVEPOINT {}", name)) + conn.execute_batch(&format!("SAVEPOINT {name}")) .map(|_| Savepoint { conn, name, @@ -267,7 +267,7 @@ impl Savepoint<'_> { #[inline] fn with_depth(conn: &Connection, depth: u32) -> Result<Savepoint<'_>> { - let name = format!("_rusqlite_sp_{}", depth); + let name = format!("_rusqlite_sp_{depth}"); Savepoint::with_depth_and_name(conn, depth, name) } @@ -552,10 +552,7 @@ mod test { } { let tx = db.transaction()?; - assert_eq!( - 2i32, - tx.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))? - ); + assert_eq!(2i32, tx.one_column::<i32>("SELECT SUM(x) FROM foo")?); } Ok(()) } @@ -591,10 +588,7 @@ mod test { tx.commit()?; } - assert_eq!( - 2i32, - db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))? - ); + assert_eq!(2i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?); Ok(()) } @@ -619,10 +613,7 @@ mod test { } { let tx = db.transaction()?; - assert_eq!( - 6i32, - tx.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))? - ); + assert_eq!(6i32, tx.one_column::<i32>("SELECT SUM(x) FROM foo")?); } Ok(()) } @@ -727,11 +718,11 @@ mod test { } fn insert(x: i32, conn: &Connection) -> Result<usize> { - conn.execute("INSERT INTO foo VALUES(?)", [x]) + conn.execute("INSERT INTO foo VALUES(?1)", [x]) } fn assert_current_sum(x: i32, conn: &Connection) -> Result<()> { - let i = conn.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?; + let i = conn.one_column::<i32>("SELECT SUM(x) FROM foo")?; assert_eq!(x, i); Ok(()) } diff --git a/src/types/chrono.rs b/src/types/chrono.rs index 6bfc2f4..6b50e01 100644 --- a/src/types/chrono.rs +++ b/src/types/chrono.rs @@ -175,12 +175,12 @@ mod test { #[test] fn test_naive_date() -> Result<()> { let db = checked_memory_handle()?; - let date = NaiveDate::from_ymd(2016, 2, 23); - db.execute("INSERT INTO foo (t) VALUES (?)", [date])?; + let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap(); + db.execute("INSERT INTO foo (t) VALUES (?1)", [date])?; - let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let s: String = db.one_column("SELECT t FROM foo")?; assert_eq!("2016-02-23", s); - let t: NaiveDate = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let t: NaiveDate = db.one_column("SELECT t FROM foo")?; assert_eq!(date, t); Ok(()) } @@ -188,12 +188,12 @@ mod test { #[test] fn test_naive_time() -> Result<()> { let db = checked_memory_handle()?; - let time = NaiveTime::from_hms(23, 56, 4); - db.execute("INSERT INTO foo (t) VALUES (?)", [time])?; + let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); + db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?; - let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let s: String = db.one_column("SELECT t FROM foo")?; assert_eq!("23:56:04", s); - let v: NaiveTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let v: NaiveTime = db.one_column("SELECT t FROM foo")?; assert_eq!(time, v); Ok(()) } @@ -201,19 +201,19 @@ mod test { #[test] fn test_naive_date_time() -> Result<()> { let db = checked_memory_handle()?; - let date = NaiveDate::from_ymd(2016, 2, 23); - let time = NaiveTime::from_hms(23, 56, 4); + let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap(); + let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); let dt = NaiveDateTime::new(date, time); - db.execute("INSERT INTO foo (t) VALUES (?)", [dt])?; + db.execute("INSERT INTO foo (t) VALUES (?1)", [dt])?; - let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let s: String = db.one_column("SELECT t FROM foo")?; assert_eq!("2016-02-23 23:56:04", s); - let v: NaiveDateTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let v: NaiveDateTime = db.one_column("SELECT t FROM foo")?; assert_eq!(dt, v); db.execute("UPDATE foo set b = datetime(t)", [])?; // "YYYY-MM-DD HH:MM:SS" - let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?; + let hms: NaiveDateTime = db.one_column("SELECT b FROM foo")?; assert_eq!(dt, hms); Ok(()) } @@ -221,28 +221,26 @@ mod test { #[test] fn test_date_time_utc() -> Result<()> { let db = checked_memory_handle()?; - let date = NaiveDate::from_ymd(2016, 2, 23); - let time = NaiveTime::from_hms_milli(23, 56, 4, 789); + let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap(); + let time = NaiveTime::from_hms_milli_opt(23, 56, 4, 789).unwrap(); let dt = NaiveDateTime::new(date, time); let utc = Utc.from_utc_datetime(&dt); - db.execute("INSERT INTO foo (t) VALUES (?)", [utc])?; + db.execute("INSERT INTO foo (t) VALUES (?1)", [utc])?; - let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let s: String = db.one_column("SELECT t FROM foo")?; assert_eq!("2016-02-23 23:56:04.789+00:00", s); - let v1: DateTime<Utc> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let v1: DateTime<Utc> = db.one_column("SELECT t FROM foo")?; assert_eq!(utc, v1); - let v2: DateTime<Utc> = - db.query_row("SELECT '2016-02-23 23:56:04.789'", [], |r| r.get(0))?; + let v2: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04.789'")?; assert_eq!(utc, v2); - let v3: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04'", [], |r| r.get(0))?; + let v3: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04'")?; assert_eq!(utc - Duration::milliseconds(789), v3); - let v4: DateTime<Utc> = - db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", [], |r| r.get(0))?; + let v4: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04.789+00:00'")?; assert_eq!(utc, v4); Ok(()) } @@ -250,18 +248,18 @@ mod test { #[test] fn test_date_time_local() -> Result<()> { let db = checked_memory_handle()?; - let date = NaiveDate::from_ymd(2016, 2, 23); - let time = NaiveTime::from_hms_milli(23, 56, 4, 789); + let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap(); + let time = NaiveTime::from_hms_milli_opt(23, 56, 4, 789).unwrap(); let dt = NaiveDateTime::new(date, time); let local = Local.from_local_datetime(&dt).single().unwrap(); - db.execute("INSERT INTO foo (t) VALUES (?)", [local])?; + db.execute("INSERT INTO foo (t) VALUES (?1)", [local])?; // Stored string should be in UTC - let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let s: String = db.one_column("SELECT t FROM foo")?; assert!(s.ends_with("+00:00")); - let v: DateTime<Local> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let v: DateTime<Local> = db.one_column("SELECT t FROM foo")?; assert_eq!(local, v); Ok(()) } @@ -271,13 +269,13 @@ mod test { let db = checked_memory_handle()?; let time = DateTime::parse_from_rfc3339("2020-04-07T11:23:45+04:00").unwrap(); - db.execute("INSERT INTO foo (t) VALUES (?)", [time])?; + db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?; // Stored string should preserve timezone offset - let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let s: String = db.one_column("SELECT t FROM foo")?; assert!(s.ends_with("+04:00")); - let v: DateTime<FixedOffset> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let v: DateTime<FixedOffset> = db.one_column("SELECT t FROM foo")?; assert_eq!(time.offset(), v.offset()); assert_eq!(time, v); Ok(()) @@ -286,38 +284,36 @@ mod test { #[test] fn test_sqlite_functions() -> Result<()> { let db = checked_memory_handle()?; - let result: Result<NaiveTime> = db.query_row("SELECT CURRENT_TIME", [], |r| r.get(0)); - assert!(result.is_ok()); - let result: Result<NaiveDate> = db.query_row("SELECT CURRENT_DATE", [], |r| r.get(0)); - assert!(result.is_ok()); - let result: Result<NaiveDateTime> = - db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0)); - assert!(result.is_ok()); - let result: Result<DateTime<Utc>> = - db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0)); - assert!(result.is_ok()); + let result: Result<NaiveTime> = db.one_column("SELECT CURRENT_TIME"); + result.unwrap(); + let result: Result<NaiveDate> = db.one_column("SELECT CURRENT_DATE"); + result.unwrap(); + let result: Result<NaiveDateTime> = db.one_column("SELECT CURRENT_TIMESTAMP"); + result.unwrap(); + let result: Result<DateTime<Utc>> = db.one_column("SELECT CURRENT_TIMESTAMP"); + result.unwrap(); Ok(()) } #[test] fn test_naive_date_time_param() -> Result<()> { let db = checked_memory_handle()?; - let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now().naive_utc()], |r| r.get(0)); - assert!(result.is_ok()); + let result: Result<bool> = db.query_row("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now().naive_utc()], |r| r.get(0)); + result.unwrap(); Ok(()) } #[test] fn test_date_time_param() -> Result<()> { let db = checked_memory_handle()?; - let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now()], |r| r.get(0)); - assert!(result.is_ok()); + let result: Result<bool> = db.query_row("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now()], |r| r.get(0)); + result.unwrap(); Ok(()) } #[test] fn test_lenient_parse_timezone() { - assert!(DateTime::<Utc>::column_result(ValueRef::Text(b"1970-01-01T00:00:00Z")).is_ok()); - assert!(DateTime::<Utc>::column_result(ValueRef::Text(b"1970-01-01T00:00:00+00")).is_ok()); + DateTime::<Utc>::column_result(ValueRef::Text(b"1970-01-01T00:00:00Z")).unwrap(); + DateTime::<Utc>::column_result(ValueRef::Text(b"1970-01-01T00:00:00+00")).unwrap(); } } diff --git a/src/types/from_sql.rs b/src/types/from_sql.rs index b95a378..91eed09 100644 --- a/src/types/from_sql.rs +++ b/src/types/from_sql.rs @@ -52,7 +52,7 @@ impl fmt::Display for FromSqlError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { FromSqlError::InvalidType => write!(f, "Invalid type"), - FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i), + FromSqlError::OutOfRange(i) => write!(f, "Value {i} out of range"), FromSqlError::InvalidBlobSize { expected_size, blob_size, @@ -244,7 +244,7 @@ mod test { { for n in out_of_range { let err = db - .query_row("SELECT ?", &[n], |r| r.get::<_, T>(0)) + .query_row("SELECT ?1", [n], |r| r.get::<_, T>(0)) .unwrap_err(); match err { Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value), @@ -254,7 +254,7 @@ mod test { for n in in_range { assert_eq!( *n, - db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0)) + db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0)) .unwrap() .into() ); diff --git a/src/types/mod.rs b/src/types/mod.rs index 4000ae2..eece984 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -102,7 +102,7 @@ mod value_ref; /// # use rusqlite::types::{Null}; /// /// fn insert_null(conn: &Connection) -> Result<usize> { -/// conn.execute("INSERT INTO people (name) VALUES (?)", [Null]) +/// conn.execute("INSERT INTO people (name) VALUES (?1)", [Null]) /// } /// ``` #[derive(Copy, Clone)] @@ -153,9 +153,9 @@ mod test { let db = checked_memory_handle()?; let v1234 = vec![1u8, 2, 3, 4]; - db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234])?; + db.execute("INSERT INTO foo(b) VALUES (?1)", [&v1234])?; - let v: Vec<u8> = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?; + let v: Vec<u8> = db.one_column("SELECT b FROM foo")?; assert_eq!(v, v1234); Ok(()) } @@ -165,9 +165,9 @@ mod test { let db = checked_memory_handle()?; let empty = vec![]; - db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty])?; + db.execute("INSERT INTO foo(b) VALUES (?1)", [&empty])?; - let v: Vec<u8> = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?; + let v: Vec<u8> = db.one_column("SELECT b FROM foo")?; assert_eq!(v, empty); Ok(()) } @@ -177,9 +177,9 @@ mod test { let db = checked_memory_handle()?; let s = "hello, world!"; - db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])?; + db.execute("INSERT INTO foo(t) VALUES (?1)", [&s])?; - let from: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let from: String = db.one_column("SELECT t FROM foo")?; assert_eq!(from, s); Ok(()) } @@ -189,9 +189,9 @@ mod test { let db = checked_memory_handle()?; let s = "hello, world!"; - db.execute("INSERT INTO foo(t) VALUES (?)", [s.to_owned()])?; + db.execute("INSERT INTO foo(t) VALUES (?1)", [s.to_owned()])?; - let from: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let from: String = db.one_column("SELECT t FROM foo")?; assert_eq!(from, s); Ok(()) } @@ -200,12 +200,9 @@ mod test { fn test_value() -> Result<()> { let db = checked_memory_handle()?; - db.execute("INSERT INTO foo(i) VALUES (?)", [Value::Integer(10)])?; + db.execute("INSERT INTO foo(i) VALUES (?1)", [Value::Integer(10)])?; - assert_eq!( - 10i64, - db.query_row::<i64, _, _>("SELECT i FROM foo", [], |r| r.get(0))? - ); + assert_eq!(10i64, db.one_column::<i64>("SELECT i FROM foo")?); Ok(()) } @@ -216,8 +213,8 @@ mod test { let s = Some("hello, world!"); let b = Some(vec![1u8, 2, 3, 4]); - db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])?; - db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])?; + db.execute("INSERT INTO foo(t) VALUES (?1)", [&s])?; + db.execute("INSERT INTO foo(b) VALUES (?1)", [&b])?; let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")?; let mut rows = stmt.query([])?; diff --git a/src/types/serde_json.rs b/src/types/serde_json.rs index a9761bd..6e38ba3 100644 --- a/src/types/serde_json.rs +++ b/src/types/serde_json.rs @@ -1,24 +1,62 @@ //! [`ToSql`] and [`FromSql`] implementation for JSON `Value`. -use serde_json::Value; +use serde_json::{Number, Value}; use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; -use crate::Result; +use crate::{Error, Result}; -/// Serialize JSON `Value` to text. +/// Serialize JSON `Value` to text: +/// +/// +/// | JSON | SQLite | +/// |----------|---------| +/// | Null | NULL | +/// | Bool | 'true' / 'false' | +/// | Number | INT or REAL except u64 | +/// | _ | TEXT | impl ToSql for Value { #[inline] fn to_sql(&self) -> Result<ToSqlOutput<'_>> { - Ok(ToSqlOutput::from(serde_json::to_string(self).unwrap())) + match self { + Value::Null => Ok(ToSqlOutput::Borrowed(ValueRef::Null)), + Value::Number(n) if n.is_i64() => Ok(ToSqlOutput::from(n.as_i64().unwrap())), + Value::Number(n) if n.is_f64() => Ok(ToSqlOutput::from(n.as_f64().unwrap())), + _ => serde_json::to_string(self) + .map(ToSqlOutput::from) + .map_err(|err| Error::ToSqlConversionFailure(err.into())), + } } } -/// Deserialize text/blob to JSON `Value`. +/// Deserialize SQLite value to JSON `Value`: +/// +/// | SQLite | JSON | +/// |----------|---------| +/// | NULL | Null | +/// | 'null' | Null | +/// | 'true' | Bool | +/// | 1 | Number | +/// | 0.1 | Number | +/// | '"text"' | String | +/// | 'text' | _Error_ | +/// | '[0, 1]' | Array | +/// | '{"x": 1}' | Object | impl FromSql for Value { #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { - let bytes = value.as_bytes()?; - serde_json::from_slice(bytes).map_err(|err| FromSqlError::Other(Box::new(err))) + match value { + ValueRef::Text(s) => serde_json::from_slice(s), // KO for b"text" + ValueRef::Blob(b) => serde_json::from_slice(b), + ValueRef::Integer(i) => Ok(Value::Number(Number::from(i))), + ValueRef::Real(f) => { + match Number::from_f64(f) { + Some(n) => Ok(Value::Number(n)), + _ => return Err(FromSqlError::InvalidType), // FIXME + } + } + ValueRef::Null => Ok(Value::Null), + } + .map_err(|err| FromSqlError::Other(Box::new(err))) } } @@ -26,6 +64,7 @@ impl FromSql for Value { mod test { use crate::types::ToSql; use crate::{Connection, Result}; + use serde_json::{Number, Value}; fn checked_memory_handle() -> Result<Connection> { let db = Connection::open_in_memory()?; @@ -38,16 +77,59 @@ mod test { let db = checked_memory_handle()?; let json = r#"{"foo": 13, "bar": "baz"}"#; - let data: serde_json::Value = serde_json::from_str(json).unwrap(); + let data: Value = serde_json::from_str(json).unwrap(); db.execute( - "INSERT INTO foo (t, b) VALUES (?, ?)", - &[&data as &dyn ToSql, &json.as_bytes()], + "INSERT INTO foo (t, b) VALUES (?1, ?2)", + [&data as &dyn ToSql, &json.as_bytes()], )?; - let t: serde_json::Value = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let t: Value = db.one_column("SELECT t FROM foo")?; assert_eq!(data, t); - let b: serde_json::Value = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?; + let b: Value = db.one_column("SELECT b FROM foo")?; assert_eq!(data, b); Ok(()) } + + #[test] + fn test_to_sql() -> Result<()> { + let db = Connection::open_in_memory()?; + + let v: Option<String> = db.query_row("SELECT ?", [Value::Null], |r| r.get(0))?; + assert_eq!(None, v); + let v: String = db.query_row("SELECT ?", [Value::Bool(true)], |r| r.get(0))?; + assert_eq!("true", v); + let v: i64 = db.query_row("SELECT ?", [Value::Number(Number::from(1))], |r| r.get(0))?; + assert_eq!(1, v); + let v: f64 = db.query_row( + "SELECT ?", + [Value::Number(Number::from_f64(0.1).unwrap())], + |r| r.get(0), + )?; + assert_eq!(0.1, v); + let v: String = + db.query_row("SELECT ?", [Value::String("text".to_owned())], |r| r.get(0))?; + assert_eq!("\"text\"", v); + Ok(()) + } + + #[test] + fn test_from_sql() -> Result<()> { + let db = Connection::open_in_memory()?; + + let v: Value = db.one_column("SELECT NULL")?; + assert_eq!(Value::Null, v); + let v: Value = db.one_column("SELECT 'null'")?; + assert_eq!(Value::Null, v); + let v: Value = db.one_column("SELECT 'true'")?; + assert_eq!(Value::Bool(true), v); + let v: Value = db.one_column("SELECT 1")?; + assert_eq!(Value::Number(Number::from(1)), v); + let v: Value = db.one_column("SELECT 0.1")?; + assert_eq!(Value::Number(Number::from_f64(0.1).unwrap()), v); + let v: Value = db.one_column("SELECT '\"text\"'")?; + assert_eq!(Value::String("text".to_owned()), v); + let v: Result<Value> = db.one_column("SELECT 'text'"); + assert!(v.is_err()); + Ok(()) + } } diff --git a/src/types/time.rs b/src/types/time.rs index 4e2811e..03b2d61 100644 --- a/src/types/time.rs +++ b/src/types/time.rs @@ -91,9 +91,9 @@ mod test { ts_vec.push(make_datetime(10_000_000_000, 0)); //November 20, 2286 for ts in ts_vec { - db.execute("INSERT INTO foo(t) VALUES (?)", [ts])?; + db.execute("INSERT INTO foo(t) VALUES (?1)", [ts])?; - let from: OffsetDateTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?; + let from: OffsetDateTime = db.one_column("SELECT t FROM foo")?; db.execute("DELETE FROM foo", [])?; @@ -143,7 +143,7 @@ mod test { Ok(OffsetDateTime::parse("2013-10-07T04:23:19.120-04:00", &Rfc3339).unwrap()), ), ] { - let result: Result<OffsetDateTime> = db.query_row("SELECT ?", [s], |r| r.get(0)); + let result: Result<OffsetDateTime> = db.query_row("SELECT ?1", [s], |r| r.get(0)); assert_eq!(result, t); } Ok(()) @@ -152,17 +152,16 @@ mod test { #[test] fn test_sqlite_functions() -> Result<()> { let db = Connection::open_in_memory()?; - let result: Result<OffsetDateTime> = - db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0)); - assert!(result.is_ok()); + let result: Result<OffsetDateTime> = db.one_column("SELECT CURRENT_TIMESTAMP"); + result.unwrap(); Ok(()) } #[test] fn test_param() -> Result<()> { let db = Connection::open_in_memory()?; - let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [OffsetDateTime::now_utc()], |r| r.get(0)); - assert!(result.is_ok()); + let result: Result<bool> = db.query_row("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [OffsetDateTime::now_utc()], |r| r.get(0)); + result.unwrap(); Ok(()) } } diff --git a/src/types/to_sql.rs b/src/types/to_sql.rs index 4e0d882..c2bc007 100644 --- a/src/types/to_sql.rs +++ b/src/types/to_sql.rs @@ -278,7 +278,7 @@ mod test { let _a: &[&dyn ToSql] = crate::params![a]; let r = ToSql::to_sql(&a); - assert!(r.is_ok()); + r.unwrap(); } #[test] @@ -287,10 +287,10 @@ mod test { let s = "str"; let cow: Cow<str> = Cow::Borrowed(s); let r = cow.to_sql(); - assert!(r.is_ok()); + r.unwrap(); let cow: Cow<str> = Cow::Owned::<str>(String::from(s)); let r = cow.to_sql(); - assert!(r.is_ok()); + r.unwrap(); // Ensure this compiles. let _p: &[&dyn ToSql] = crate::params![cow]; } @@ -301,7 +301,7 @@ mod test { let _s: &[&dyn ToSql] = crate::params![s]; let r = ToSql::to_sql(&s); - assert!(r.is_ok()); + r.unwrap(); } #[test] @@ -310,7 +310,7 @@ mod test { let _s: &[&dyn ToSql] = crate::params![s]; let r = s.to_sql(); - assert!(r.is_ok()); + r.unwrap(); } #[test] @@ -319,7 +319,7 @@ mod test { let _s: &[&dyn ToSql] = crate::params![s]; let r = ToSql::to_sql(&s); - assert!(r.is_ok()); + r.unwrap(); } #[test] @@ -331,32 +331,32 @@ mod test { let s: Rc<Box<str>> = Rc::new(source_str.clone()); let _s: &[&dyn ToSql] = crate::params![s]; let r = s.to_sql(); - assert!(r.is_ok()); + r.unwrap(); let s: Arc<Box<str>> = Arc::new(source_str.clone()); let _s: &[&dyn ToSql] = crate::params![s]; let r = s.to_sql(); - assert!(r.is_ok()); + r.unwrap(); let s: Arc<str> = Arc::from(&*source_str); let _s: &[&dyn ToSql] = crate::params![s]; let r = s.to_sql(); - assert!(r.is_ok()); + r.unwrap(); let s: Arc<dyn ToSql> = Arc::new(source_str.clone()); let _s: &[&dyn ToSql] = crate::params![s]; let r = s.to_sql(); - assert!(r.is_ok()); + r.unwrap(); let s: Rc<str> = Rc::from(&*source_str); let _s: &[&dyn ToSql] = crate::params![s]; let r = s.to_sql(); - assert!(r.is_ok()); + r.unwrap(); let s: Rc<dyn ToSql> = Rc::new(source_str); let _s: &[&dyn ToSql] = crate::params![s]; let r = s.to_sql(); - assert!(r.is_ok()); + r.unwrap(); } #[cfg(feature = "i128_blob")] @@ -368,10 +368,10 @@ mod test { db.execute( " INSERT INTO foo(i128, desc) VALUES - (?, 'zero'), - (?, 'neg one'), (?, 'neg two'), - (?, 'pos one'), (?, 'pos two'), - (?, 'min'), (?, 'max')", + (?1, 'zero'), + (?2, 'neg one'), (?3, 'neg two'), + (?4, 'pos one'), (?5, 'pos two'), + (?6, 'min'), (?7, 'max')", [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX], )?; @@ -410,11 +410,11 @@ mod test { let id = Uuid::new_v4(); db.execute( - "INSERT INTO foo (id, label) VALUES (?, ?)", + "INSERT INTO foo (id, label) VALUES (?1, ?2)", params![id, "target"], )?; - let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?")?; + let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?1")?; let mut rows = stmt.query(params![id])?; let row = rows.next()?.unwrap(); diff --git a/src/types/url.rs b/src/types/url.rs index fea8500..0ebb59b 100644 --- a/src/types/url.rs +++ b/src/types/url.rs @@ -49,7 +49,7 @@ mod test { let url2 = "http://www.example2.com/👌"; db.execute( - "INSERT INTO urls (i, v) VALUES (0, ?), (1, ?), (2, ?), (3, ?)", + "INSERT INTO urls (i, v) VALUES (0, ?1), (1, ?2), (2, ?3), (3, ?4)", // also insert a non-hex encoded url (which might be present if it was // inserted separately) params![url0, url1, url2, "illegal"], diff --git a/src/unlock_notify.rs b/src/unlock_notify.rs index 8fba6b3..065c52d 100644 --- a/src/unlock_notify.rs +++ b/src/unlock_notify.rs @@ -109,8 +109,8 @@ mod test { tx2.commit().unwrap(); }); assert_eq!(tx.recv().unwrap(), 1); - let the_answer: Result<i64> = db1.query_row("SELECT x FROM foo", [], |r| r.get(0)); - assert_eq!(42i64, the_answer?); + let the_answer: i64 = db1.one_column("SELECT x FROM foo")?; + assert_eq!(42i64, the_answer); child.join().unwrap(); Ok(()) } diff --git a/src/util/mod.rs b/src/util/mod.rs index 2b8dcfd..e81e3c0 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -5,7 +5,5 @@ pub(crate) use param_cache::ParamIndexCache; pub(crate) use small_cstr::SmallCString; // Doesn't use any modern features or vtab stuff, but is only used by them. -#[cfg(any(feature = "modern_sqlite", feature = "vtab"))] mod sqlite_string; -#[cfg(any(feature = "modern_sqlite", feature = "vtab"))] pub(crate) use sqlite_string::SqliteMallocString; diff --git a/src/util/small_cstr.rs b/src/util/small_cstr.rs index 78e43bd..1ec7374 100644 --- a/src/util/small_cstr.rs +++ b/src/util/small_cstr.rs @@ -163,8 +163,8 @@ mod test { assert_eq!(SmallCString::new("").unwrap().0.as_slice(), b"\0"); assert_eq!(SmallCString::new("").unwrap().as_bytes_without_nul(), b""); - assert!(SmallCString::new("\0").is_err()); - assert!(SmallCString::new("\0abc").is_err()); - assert!(SmallCString::new("abc\0").is_err()); + SmallCString::new("\0").unwrap_err(); + SmallCString::new("\0abc").unwrap_err(); + SmallCString::new("abc\0").unwrap_err(); } } diff --git a/src/util/sqlite_string.rs b/src/util/sqlite_string.rs index da261ba..ae24c28 100644 --- a/src/util/sqlite_string.rs +++ b/src/util/sqlite_string.rs @@ -1,10 +1,7 @@ // This is used when either vtab or modern-sqlite is on. Different methods are // used in each feature. Avoid having to track this for each function. We will // still warn for anything that's not used by either, though. -#![cfg_attr( - not(all(feature = "vtab", feature = "modern-sqlite")), - allow(dead_code) -)] +#![cfg_attr(not(feature = "vtab"), allow(dead_code))] use crate::ffi; use std::marker::PhantomData; use std::os::raw::{c_char, c_int}; @@ -134,7 +131,8 @@ impl SqliteMallocString { // (everything is aligned to 1) // - `size` is also never zero, although this function doesn't actually require // it now. - let layout = Layout::from_size_align_unchecked(s.len().saturating_add(1), 1); + let len = s.len().saturating_add(1).min(isize::MAX as usize); + let layout = Layout::from_size_align_unchecked(len, 1); // Note: This call does not return. handle_alloc_error(layout); }); @@ -214,7 +212,7 @@ mod test { let mut v = vec![]; for i in 0..1000 { v.push(SqliteMallocString::from_str(&i.to_string()).into_raw()); - v.push(SqliteMallocString::from_str(&format!("abc {} 😀", i)).into_raw()); + v.push(SqliteMallocString::from_str(&format!("abc {i} 😀")).into_raw()); } unsafe { for (i, s) in v.chunks_mut(2).enumerate() { @@ -226,7 +224,7 @@ mod test { ); assert_eq!( std::ffi::CStr::from_ptr(s1).to_str().unwrap(), - &format!("abc {} 😀", i) + &format!("abc {i} 😀") ); let _ = SqliteMallocString::from_raw(s0).unwrap(); let _ = SqliteMallocString::from_raw(s1).unwrap(); diff --git a/src/vtab/array.rs b/src/vtab/array.rs index f09ac1a..be19a3e 100644 --- a/src/vtab/array.rs +++ b/src/vtab/array.rs @@ -17,7 +17,7 @@ //! let v = [1i64, 2, 3, 4]; //! // Note: A `Rc<Vec<Value>>` must be used as the parameter. //! let values = Rc::new(v.iter().copied().map(Value::from).collect::<Vec<Value>>()); -//! let mut stmt = db.prepare("SELECT value from rarray(?);")?; +//! let mut stmt = db.prepare("SELECT value from rarray(?1);")?; //! let rows = stmt.query_map([values], |row| row.get::<_, i64>(0))?; //! for value in rows { //! println!("{}", value?); @@ -206,9 +206,9 @@ mod test { let values: Vec<Value> = v.into_iter().map(Value::from).collect(); let ptr = Rc::new(values); { - let mut stmt = db.prepare("SELECT value from rarray(?);")?; + let mut stmt = db.prepare("SELECT value from rarray(?1);")?; - let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0))?; + let rows = stmt.query_map([&ptr], |row| row.get::<_, i64>(0))?; assert_eq!(2, Rc::strong_count(&ptr)); let mut count = 0; for (i, value) in rows.enumerate() { diff --git a/src/vtab/csvtab.rs b/src/vtab/csvtab.rs index a65db05..843363c 100644 --- a/src/vtab/csvtab.rs +++ b/src/vtab/csvtab.rs @@ -208,13 +208,13 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab { let mut record = csv::ByteRecord::new(); if reader.read_byte_record(&mut record)? { for (i, _) in record.iter().enumerate() { - cols.push(format!("c{}", i)); + cols.push(format!("c{i}")); } } } } else if let Some(n_col) = n_col { for i in 0..n_col { - cols.push(format!("c{}", i)); + cols.push(format!("c{i}")); } } diff --git a/src/vtab/mod.rs b/src/vtab/mod.rs index 07008f3..f070e3a 100644 --- a/src/vtab/mod.rs +++ b/src/vtab/mod.rs @@ -187,7 +187,6 @@ pub fn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab, /// Virtual table configuration options #[repr(i32)] #[non_exhaustive] -#[cfg(feature = "modern_sqlite")] // 3.7.7 #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum VTabConfig { /// Equivalent to SQLITE_VTAB_CONSTRAINT_SUPPORT @@ -203,8 +202,6 @@ pub struct VTabConnection(*mut ffi::sqlite3); impl VTabConnection { /// Configure various facets of the virtual table interface - #[cfg(feature = "modern_sqlite")] // 3.7.7 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] pub fn config(&mut self, config: VTabConfig) -> Result<()> { crate::error::check(unsafe { ffi::sqlite3_vtab_config(self.0, config as c_int) }) } @@ -369,7 +366,6 @@ impl From<u8> for IndexConstraintOp { } } -#[cfg(feature = "modern_sqlite")] // 3.9.0 bitflags::bitflags! { /// Virtual table scan flags /// See [Function Flags](https://sqlite.org/c3ref/c_index_scan_unique.html) for details. @@ -461,7 +457,7 @@ impl IndexInfo { #[inline] pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) { unsafe { - (*self.0).orderByConsumed = if order_by_consumed { 1 } else { 0 }; + (*self.0).orderByConsumed = order_by_consumed as c_int; } } @@ -474,8 +470,6 @@ impl IndexInfo { } /// Estimated number of rows returned. - #[cfg(feature = "modern_sqlite")] // SQLite >= 3.8.2 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] #[inline] pub fn set_estimated_rows(&mut self, estimated_rows: i64) { unsafe { @@ -484,16 +478,12 @@ impl IndexInfo { } /// Mask of SQLITE_INDEX_SCAN_* flags. - #[cfg(feature = "modern_sqlite")] // SQLite >= 3.9.0 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] #[inline] pub fn set_idx_flags(&mut self, flags: IndexFlags) { unsafe { (*self.0).idxFlags = flags.bits() }; } /// Mask of columns used by statement - #[cfg(feature = "modern_sqlite")] // SQLite >= 3.10.0 - #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] #[inline] pub fn col_used(&self) -> u64 { unsafe { (*self.0).colUsed } @@ -509,7 +499,7 @@ impl IndexInfo { if collation.is_null() { return Err(Error::SqliteFailure( ffi::Error::new(ffi::SQLITE_MISUSE), - Some(format!("{} is out of range", constraint_idx)), + Some(format!("{constraint_idx} is out of range")), )); } Ok(unsafe { CStr::from_ptr(collation) }.to_str()?) @@ -623,13 +613,13 @@ impl IndexConstraintUsage<'_> { /// if `omit`, do not code a test for this constraint #[inline] pub fn set_omit(&mut self, omit: bool) { - self.0.omit = if omit { 1 } else { 0 }; + self.0.omit = omit as std::os::raw::c_uchar; } } /// `feature = "vtab"` pub struct OrderByIter<'a> { - iter: slice::Iter<'a, ffi::sqlite3_index_info_sqlite3_index_orderby>, + iter: slice::Iter<'a, ffi::sqlite3_index_orderby>, } impl<'a> Iterator for OrderByIter<'a> { @@ -647,7 +637,7 @@ impl<'a> Iterator for OrderByIter<'a> { } /// A column of the ORDER BY clause. -pub struct OrderBy<'a>(&'a ffi::sqlite3_index_info_sqlite3_index_orderby); +pub struct OrderBy<'a>(&'a ffi::sqlite3_index_orderby); impl OrderBy<'_> { /// Column number @@ -934,7 +924,7 @@ pub fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> { return Ok((param, value)); } } - Err(Error::ModuleError(format!("illegal argument: '{}'", arg))) + Err(Error::ModuleError(format!("illegal argument: '{arg}'"))) } // FIXME copy/paste from function.rs @@ -1334,7 +1324,7 @@ pub mod csvtab; #[cfg(feature = "series")] #[cfg_attr(docsrs, doc(cfg(feature = "series")))] pub mod series; // SQLite >= 3.9.0 -#[cfg(test)] +#[cfg(all(test, feature = "modern_sqlite"))] mod vtablog; #[cfg(test)] diff --git a/src/vtab/series.rs b/src/vtab/series.rs index fffbd4d..4b1993e 100644 --- a/src/vtab/series.rs +++ b/src/vtab/series.rs @@ -28,6 +28,7 @@ const SERIES_COLUMN_STOP: c_int = 2; const SERIES_COLUMN_STEP: c_int = 3; bitflags::bitflags! { + #[derive(Clone, Copy)] #[repr(C)] struct QueryPlanFlags: ::std::os::raw::c_int { // start = $value -- constraint exists @@ -41,7 +42,7 @@ bitflags::bitflags! { // output in ascending order const ASC = 16; // Both start and stop - const BOTH = QueryPlanFlags::START.bits | QueryPlanFlags::STOP.bits; + const BOTH = QueryPlanFlags::START.bits() | QueryPlanFlags::STOP.bits(); } } @@ -115,6 +116,7 @@ unsafe impl<'vtab> VTab<'vtab> for SeriesTab { } if idx_num.contains(QueryPlanFlags::BOTH) { // Both start= and stop= boundaries are available. + #[allow(clippy::bool_to_int_with_if)] info.set_estimated_cost(f64::from( 2 - if idx_num.contains(QueryPlanFlags::STEP) { 1 diff --git a/src/vtab/vtablog.rs b/src/vtab/vtablog.rs index bc2e01f..1b3e1b8 100644 --- a/src/vtab/vtablog.rs +++ b/src/vtab/vtablog.rs @@ -153,7 +153,7 @@ impl<'vtab> CreateVTab<'vtab> for VTabLog { impl<'vtab> UpdateVTab<'vtab> for VTabLog { fn delete(&mut self, arg: ValueRef<'_>) -> Result<()> { - println!("VTabLog::delete({}, {:?})", self.i_inst, arg); + println!("VTabLog::delete({}, {arg:?})", self.i_inst); Ok(()) } @@ -163,7 +163,7 @@ impl<'vtab> UpdateVTab<'vtab> for VTabLog { self.i_inst, args.iter().collect::<Vec<ValueRef<'_>>>() ); - Ok(self.n_row as i64) + Ok(self.n_row) } fn update(&mut self, args: &Values<'_>) -> Result<()> { @@ -246,7 +246,7 @@ unsafe impl VTabCursor for VTabLogCursor<'_> { self.row_id ) } else { - format!("{}{}", i, self.row_id) + format!("{i}{}", self.row_id) }; println!( "VTabLogCursor::column(tab={}, cursor={}, i={}): {}", @@ -286,13 +286,13 @@ mod test { let mut stmt = db.prepare("SELECT * FROM log;")?; let mut rows = stmt.query([])?; while rows.next()?.is_some() {} - db.execute("DELETE FROM log WHERE a = ?", ["a1"])?; + db.execute("DELETE FROM log WHERE a = ?1", ["a1"])?; db.execute( - "INSERT INTO log (a, b, c) VALUES (?, ?, ?)", + "INSERT INTO log (a, b, c) VALUES (?1, ?2, ?3)", ["a", "b", "c"], )?; db.execute( - "UPDATE log SET b = ?, c = ? WHERE a = ?", + "UPDATE log SET b = ?1, c = ?2 WHERE a = ?3", ["bn", "cn", "a1"], )?; Ok(()) diff --git a/tests/deny_single_threaded_sqlite_config.rs b/tests/deny_single_threaded_sqlite_config.rs index adfc8e5..7118dab 100644 --- a/tests/deny_single_threaded_sqlite_config.rs +++ b/tests/deny_single_threaded_sqlite_config.rs @@ -16,5 +16,5 @@ fn test_error_when_singlethread_mode() { assert_eq!(ffi::sqlite3_initialize(), ffi::SQLITE_OK); } let res = Connection::open_in_memory(); - assert!(res.is_err()); + res.unwrap_err(); } |