aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Walbran <qwandor@google.com>2023-05-30 22:07:03 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-05-30 22:07:03 +0000
commit710a98d1f5c07c1f0c5c03f2a1f825d6f41bd55a (patch)
treef16f7b4a65385063acd5c2e6ad3b8a01f47d6411
parent02347db81ada5d1e955169e8dbb4e235af6843fa (diff)
parentbc9c9a01f7580246c48ec573e7c7952d66796dc8 (diff)
downloadrusqlite-710a98d1f5c07c1f0c5c03f2a1f825d6f41bd55a.tar.gz
Update to 0.29.0. am: 3de7850ae0 am: e576f2428a am: 4c9427463d am: 105a1fe1ca am: bc9c9a01f7
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/rusqlite/+/2605765 Change-Id: I68ab3dd1608fe46cba6baa45e93bbd543fe512ec Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp4
-rw-r--r--Cargo.toml28
-rw-r--r--Cargo.toml.orig20
-rw-r--r--METADATA10
-rw-r--r--README.md14
-rw-r--r--patches/Android.bp.diff13
-rw-r--r--src/backup.rs12
-rw-r--r--src/blob/mod.rs18
-rw-r--r--src/blob/pos_io.rs32
-rw-r--r--src/busy.rs6
-rw-r--r--src/cache.rs4
-rw-r--r--src/column.rs5
-rw-r--r--src/config.rs8
-rw-r--r--src/error.rs26
-rw-r--r--src/functions.rs52
-rw-r--r--src/hooks.rs14
-rw-r--r--src/inner_connection.rs27
-rw-r--r--src/lib.rs250
-rw-r--r--src/limits.rs34
-rw-r--r--src/params.rs11
-rw-r--r--src/pragma.rs33
-rw-r--r--src/raw_statement.rs9
-rw-r--r--src/row.rs60
-rw-r--r--src/session.rs41
-rw-r--r--src/statement.rs317
-rw-r--r--src/trace.rs8
-rw-r--r--src/transaction.rs23
-rw-r--r--src/types/chrono.rs92
-rw-r--r--src/types/from_sql.rs6
-rw-r--r--src/types/mod.rs29
-rw-r--r--src/types/serde_json.rs106
-rw-r--r--src/types/time.rs15
-rw-r--r--src/types/to_sql.rs36
-rw-r--r--src/types/url.rs2
-rw-r--r--src/unlock_notify.rs4
-rw-r--r--src/util/mod.rs2
-rw-r--r--src/util/small_cstr.rs6
-rw-r--r--src/util/sqlite_string.rs12
-rw-r--r--src/vtab/array.rs6
-rw-r--r--src/vtab/csvtab.rs4
-rw-r--r--src/vtab/mod.rs24
-rw-r--r--src/vtab/series.rs4
-rw-r--r--src/vtab/vtablog.rs12
-rw-r--r--tests/deny_single_threaded_sqlite_config.rs2
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
diff --git a/Android.bp b/Android.bp
index ca704b4..a1cdbea 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
diff --git a/Cargo.toml b/Cargo.toml
index 08b3bc0..28b8336 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/METADATA b/METADATA
index ea56c4e..17cfd9b 100644
--- a/METADATA
+++ b/METADATA
@@ -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
}
}
diff --git a/README.md b/README.md
index fdc2381..76013b8 100644
--- a/README.md
+++ b/README.md
@@ -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();
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 89f133e..1d982f6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)) }
}
diff --git a/src/row.rs b/src/row.rs
index 221905a..93cf824 100644
--- a/src/row.rs
+++ b/src/row.rs
@@ -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();
}