diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 0000000000..1c6131d4a7 --- /dev/null +++ b/.github/workflows/clippy.yml @@ -0,0 +1,86 @@ +name: Clippy + +on: + push: + pull_request: + +permissions: + contents: read + checks: write + +env: + CARGO_TERM_COLOR: always + +jobs: + clippy-rt: + name: clippy (sqlx-rt-oldapi) • ${{ matrix.rt }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rt: + - runtime-tokio-native-tls + - runtime-tokio-rustls + - runtime-async-std-native-tls + - runtime-async-std-rustls + steps: + - uses: actions/checkout@v4 + - name: Install system deps + run: sudo apt-get update -y && sudo apt-get install -y pkg-config libssl-dev + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + with: + cache-all-crates: true + - name: Run clippy (rt) + run: | + cargo clippy -p sqlx-rt-oldapi --no-default-features --features "${{ matrix.rt }}" --all-targets -- -D warnings + + clippy-core: + name: clippy (sqlx-core-oldapi) • ${{ matrix.db }} • ${{ matrix.rt }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + db: [postgres, mysql, sqlite, mssql, any] + rt: [runtime-tokio-rustls] + steps: + - uses: actions/checkout@v4 + - name: Install system deps + run: sudo apt-get update -y && sudo apt-get install -y pkg-config libssl-dev + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + with: + cache-all-crates: true + - name: Run clippy (core) + run: | + cargo clippy -p sqlx-core-oldapi --no-default-features --features "${{ matrix.rt }},${{ matrix.db }}" --all-targets -- -D warnings + + clippy-root: + name: clippy (sqlx-oldapi) • ${{ matrix.combo }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + combo: + - "runtime-tokio-rustls,postgres,macros" + - "runtime-tokio-rustls,sqlite,macros" + - "runtime-tokio-rustls,mysql,macros" + - "runtime-async-std-rustls,postgres,macros" + steps: + - uses: actions/checkout@v4 + - name: Install system deps + run: sudo apt-get update -y && sudo apt-get install -y pkg-config libssl-dev + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy + - uses: Swatinem/rust-cache@v2 + with: + cache-all-crates: true + - name: Run clippy (root) + run: | + cargo clippy -p sqlx-oldapi --no-default-features --features "${{ matrix.combo }}" --all-targets -- -D warnings + diff --git a/examples/postgres/axum-social-with-tests/tests/common.rs b/examples/postgres/axum-social-with-tests/tests/common.rs index 41abd3af10..95c8e6a32f 100644 --- a/examples/postgres/axum-social-with-tests/tests/common.rs +++ b/examples/postgres/axum-social-with-tests/tests/common.rs @@ -1,5 +1,4 @@ // This is imported by different tests that use different functions. -#![allow(dead_code)] use axum::body::{Body, BoxBody, HttpBody}; use axum::http::header::CONTENT_TYPE; diff --git a/sqlx-core/src/any/connection/executor.rs b/sqlx-core/src/any/connection/executor.rs index 560d818010..f9058aa997 100644 --- a/sqlx-core/src/any/connection/executor.rs +++ b/sqlx-core/src/any/connection/executor.rs @@ -14,12 +14,13 @@ use futures_util::{StreamExt, TryStreamExt}; impl<'c> Executor<'c> for &'c mut AnyConnection { type Database = Any; - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, mut query: E, ) -> BoxStream<'e, Result, Error>> where 'c: 'e, + 'q: 'e, E: Execute<'q, Self::Database>, { let arguments = query.take_arguments(); @@ -52,12 +53,10 @@ impl<'c> Executor<'c> for &'c mut AnyConnection { } } - fn fetch_optional<'e, 'q: 'e, E: 'q>( - self, - mut query: E, - ) -> BoxFuture<'e, Result, Error>> + fn fetch_optional<'e, 'q, E>(self, mut query: E) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, E: Execute<'q, Self::Database>, { let arguments = query.take_arguments(); @@ -92,13 +91,14 @@ impl<'c> Executor<'c> for &'c mut AnyConnection { }) } - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, sql: &'q str, _parameters: &[AnyTypeInfo], ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { Ok(match &mut self.0 { @@ -118,12 +118,13 @@ impl<'c> Executor<'c> for &'c mut AnyConnection { }) } - fn describe<'e, 'q: 'e>( + fn describe<'e, 'q>( self, sql: &'q str, ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { Ok(match &mut self.0 { diff --git a/sqlx-core/src/any/statement.rs b/sqlx-core/src/any/statement.rs index 0c283c2e5e..533702d92c 100644 --- a/sqlx-core/src/any/statement.rs +++ b/sqlx-core/src/any/statement.rs @@ -33,7 +33,7 @@ impl<'q> Statement<'q> for AnyStatement<'q> { fn parameters(&self) -> Option> { match &self.parameters { - Some(Either::Left(types)) => Some(Either::Left(&types)), + Some(Either::Left(types)) => Some(Either::Left(types)), Some(Either::Right(count)) => Some(Either::Right(*count)), None => None, } @@ -55,6 +55,6 @@ where .column_names .get(*self) .ok_or_else(|| Error::ColumnNotFound((*self).into())) - .map(|v| *v) + .copied() } } diff --git a/sqlx-core/src/common/mod.rs b/sqlx-core/src/common/mod.rs index 63ed52815b..0af9daea5d 100644 --- a/sqlx-core/src/common/mod.rs +++ b/sqlx-core/src/common/mod.rs @@ -1,12 +1,16 @@ mod statement_cache; pub(crate) use statement_cache::StatementCache; +#[cfg(feature = "sqlite")] use std::fmt::{Debug, Formatter}; +#[cfg(feature = "sqlite")] use std::ops::{Deref, DerefMut}; /// A wrapper for `Fn`s that provides a debug impl that just says "Function" +#[cfg(feature = "sqlite")] pub(crate) struct DebugFn(pub F); +#[cfg(feature = "sqlite")] impl Deref for DebugFn { type Target = F; @@ -15,12 +19,14 @@ impl Deref for DebugFn { } } +#[cfg(feature = "sqlite")] impl DerefMut for DebugFn { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } +#[cfg(feature = "sqlite")] impl Debug for DebugFn { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Function").finish() diff --git a/sqlx-core/src/connection.rs b/sqlx-core/src/connection.rs index c92145eb92..0b701bc3ac 100644 --- a/sqlx-core/src/connection.rs +++ b/sqlx-core/src/connection.rs @@ -116,10 +116,11 @@ pub trait Connection: Send { fn connect(url: &str) -> BoxFuture<'static, Result> where Self: Sized, + Self::Options: FromStr, { let options = url.parse(); - Box::pin(async move { Ok(Self::connect_with(&options?).await?) }) + Box::pin(async move { Self::connect_with(&options?).await }) } /// Establish a new database connection with the provided options. @@ -158,24 +159,31 @@ impl LogSettings { } } -pub trait ConnectOptions: 'static + Send + Sync + FromStr + Debug + Clone { - type Connection: Connection + ?Sized; +pub trait ConnectOptions: Sized + Send + Sync + 'static { + type Connection: Connection; - /// Establish a new database connection with the options specified by `self`. - fn connect(&self) -> BoxFuture<'_, Result> + fn from_url(url: &str) -> Result where - Self::Connection: Sized; + Self: std::str::FromStr, + { + Self::from_str(url) + } - /// Log executed statements with the specified `level` - fn log_statements(&mut self, level: LevelFilter) -> &mut Self; + fn connect(&self) -> BoxFuture<'_, Result>; - /// Log executed statements with a duration above the specified `duration` - /// at the specified `level`. - fn log_slow_statements(&mut self, level: LevelFilter, duration: Duration) -> &mut Self; + fn connect_with(options: &Self) -> BoxFuture<'_, Result> { + options.connect() + } - /// Entirely disables statement logging (both slow and regular). - fn disable_statement_logging(&mut self) -> &mut Self { - self.log_statements(LevelFilter::Off) - .log_slow_statements(LevelFilter::Off, Duration::default()) + fn from_env() -> Result + where + Self: std::str::FromStr, + { + let url = std::env::var("DATABASE_URL").map_err(Error::config)?; + Self::from_str(&url) } + + fn log_statements(&mut self, level: LevelFilter) -> &mut Self; + + fn log_slow_statements(&mut self, level: LevelFilter, duration: Duration) -> &mut Self; } diff --git a/sqlx-core/src/executor.rs b/sqlx-core/src/executor.rs index 2b0e27c219..42875f333c 100644 --- a/sqlx-core/src/executor.rs +++ b/sqlx-core/src/executor.rs @@ -26,25 +26,27 @@ pub trait Executor<'c>: Send + Debug + Sized { type Database: Database; /// Execute the query and return the total number of rows affected. - fn execute<'e, 'q: 'e, E: 'q>( + fn execute<'e, 'q, E>( self, query: E, ) -> BoxFuture<'e, Result<::QueryResult, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { self.execute_many(query).try_collect().boxed() } /// Execute multiple queries and return the rows affected from each query, in a stream. - fn execute_many<'e, 'q: 'e, E: 'q>( + fn execute_many<'e, 'q, E>( self, query: E, ) -> BoxStream<'e, Result<::QueryResult, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { self.fetch_many(query) .try_filter_map(|step| async move { @@ -57,13 +59,14 @@ pub trait Executor<'c>: Send + Debug + Sized { } /// Execute the query and return the generated results as a stream. - fn fetch<'e, 'q: 'e, E: 'q>( + fn fetch<'e, 'q, E>( self, query: E, ) -> BoxStream<'e, Result<::Row, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { self.fetch_many(query) .try_filter_map(|step| async move { @@ -77,7 +80,7 @@ pub trait Executor<'c>: Send + Debug + Sized { /// Execute multiple queries and return the generated results as a stream /// from each query, in a stream. - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, query: E, ) -> BoxStream< @@ -89,28 +92,31 @@ pub trait Executor<'c>: Send + Debug + Sized { > where 'c: 'e, - E: Execute<'q, Self::Database>; + 'q: 'e, + E: Execute<'q, Self::Database> + 'e; /// Execute the query and return all the generated results, collected into a [`Vec`]. - fn fetch_all<'e, 'q: 'e, E: 'q>( + fn fetch_all<'e, 'q, E>( self, query: E, ) -> BoxFuture<'e, Result::Row>, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { self.fetch(query).try_collect().boxed() } /// Execute the query and returns exactly one row. - fn fetch_one<'e, 'q: 'e, E: 'q>( + fn fetch_one<'e, 'q, E>( self, query: E, ) -> BoxFuture<'e, Result<::Row, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { self.fetch_optional(query) .and_then(|row| match row { @@ -121,13 +127,14 @@ pub trait Executor<'c>: Send + Debug + Sized { } /// Execute the query and returns at most one row. - fn fetch_optional<'e, 'q: 'e, E: 'q>( + fn fetch_optional<'e, 'q, E>( self, query: E, ) -> BoxFuture<'e, Result::Row>, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>; + 'q: 'e, + E: Execute<'q, Self::Database> + 'e; /// Prepare the SQL query to inspect the type information of its parameters /// and results. @@ -138,12 +145,13 @@ pub trait Executor<'c>: Send + Debug + Sized { /// This explicit API is provided to allow access to the statement metadata available after /// it prepared but before the first row is returned. #[inline] - fn prepare<'e, 'q: 'e>( + fn prepare<'e, 'q>( self, query: &'q str, ) -> BoxFuture<'e, Result<>::Statement, Error>> where 'c: 'e, + 'q: 'e, { self.prepare_with(query, &[]) } @@ -153,13 +161,14 @@ pub trait Executor<'c>: Send + Debug + Sized { /// /// Only some database drivers (PostgreSQL, MSSQL) can take advantage of /// this extra information to influence parameter type inference. - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, sql: &'q str, parameters: &'e [::TypeInfo], ) -> BoxFuture<'e, Result<>::Statement, Error>> where - 'c: 'e; + 'c: 'e, + 'q: 'e; /// Describe the SQL query and return type information about its parameters /// and results. @@ -167,12 +176,13 @@ pub trait Executor<'c>: Send + Debug + Sized { /// This is used by compile-time verification in the query macros to /// power their type inference. #[doc(hidden)] - fn describe<'e, 'q: 'e>( + fn describe<'e, 'q>( self, sql: &'q str, ) -> BoxFuture<'e, Result, Error>> where - 'c: 'e; + 'c: 'e, + 'q: 'e; } /// A type that may be executed against a database connection. diff --git a/sqlx-core/src/io/buf.rs b/sqlx-core/src/io/buf.rs index 7aa3289ecd..2446ce61e7 100644 --- a/sqlx-core/src/io/buf.rs +++ b/sqlx-core/src/io/buf.rs @@ -22,7 +22,7 @@ pub trait BufExt: Buf { impl BufExt for Bytes { fn get_bytes_nul(&mut self) -> Result { let nul = - memchr(b'\0', &self).ok_or_else(|| err_protocol!("expected NUL in byte sequence"))?; + memchr(b'\0', self).ok_or_else(|| err_protocol!("expected NUL in byte sequence"))?; let v = self.slice(0..nul); @@ -40,7 +40,7 @@ impl BufExt for Bytes { fn get_str_nul(&mut self) -> Result { self.get_bytes_nul().and_then(|bytes| { - from_utf8(&*bytes) + from_utf8(&bytes) .map(ToOwned::to_owned) .map_err(|err| err_protocol!("{}", err)) }) diff --git a/sqlx-core/src/io/buf_stream.rs b/sqlx-core/src/io/buf_stream.rs index 8f376cbfb0..ca2d4f358f 100644 --- a/sqlx-core/src/io/buf_stream.rs +++ b/sqlx-core/src/io/buf_stream.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use std::io; use std::ops::{Deref, DerefMut}; @@ -37,6 +35,7 @@ where } } + #[allow(dead_code)] pub fn write<'en, T>(&mut self, value: T) where T: Encode<'en, ()>, @@ -62,9 +61,11 @@ where where T: Decode<'de, ()>, { - self.read_with(cnt, ()).await + let buf = self.read_raw(cnt).await?.freeze(); + T::decode(buf) } + #[allow(dead_code)] pub async fn read_with<'de, T, C>(&mut self, cnt: usize, context: C) -> Result where T: Decode<'de, C>, @@ -79,6 +80,7 @@ where Ok(buf) } + #[allow(dead_code)] pub async fn read_raw_into(&mut self, buf: &mut BytesMut, cnt: usize) -> Result<(), Error> { read_raw_into(&mut self.stream, buf, cnt).await } diff --git a/sqlx-core/src/io/mod.rs b/sqlx-core/src/io/mod.rs index 3900d38e0d..f994965400 100644 --- a/sqlx-core/src/io/mod.rs +++ b/sqlx-core/src/io/mod.rs @@ -1,4 +1,3 @@ -#![allow(unused_imports)] mod buf; mod buf_mut; mod buf_stream; diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index 1056fbde1d..b44fee6098 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -2,7 +2,7 @@ //! Not intended to be used directly. #![recursion_limit = "512"] #![warn(future_incompatible, rust_2018_idioms)] -#![allow(clippy::needless_doctest_main, clippy::type_complexity, dead_code)] +#![allow(clippy::needless_doctest_main, clippy::type_complexity)] #![deny( clippy::cast_possible_truncation, clippy::cast_possible_wrap, diff --git a/sqlx-core/src/logger.rs b/sqlx-core/src/logger.rs index df36ff73aa..67430e4ee4 100644 --- a/sqlx-core/src/logger.rs +++ b/sqlx-core/src/logger.rs @@ -47,11 +47,11 @@ impl<'q> QueryLogger<'q> { .to_level() .filter(|lvl| log::log_enabled!(target: "sqlx::query", *lvl)) { - let mut summary = parse_query_summary(&self.sql); + let mut summary = parse_query_summary(self.sql); let sql = if summary != self.sql { summary.push_str(" …"); - format!("\n\n{}\n", &self.sql) + format!("\n\n{}\n", self.sql) } else { String::new() }; @@ -99,16 +99,13 @@ impl<'q, O: Debug + Hash + Eq, R: Debug, P: Debug> QueryPlanLogger<'q, O, R, P> } pub(crate) fn log_enabled(&self) -> bool { - if let Some(_lvl) = self - .settings - .statements_level - .to_level() - .filter(|lvl| log::log_enabled!(target: "sqlx::explain", *lvl)) - { - return true; - } else { - return false; - } + matches!( + self.settings + .statements_level + .to_level() + .filter(|lvl| log::log_enabled!(target: "sqlx::explain", *lvl)), + Some(_) + ) } pub(crate) fn add_result(&mut self, result: R) { @@ -126,11 +123,11 @@ impl<'q, O: Debug + Hash + Eq, R: Debug, P: Debug> QueryPlanLogger<'q, O, R, P> .to_level() .filter(|lvl| log::log_enabled!(target: "sqlx::explain", *lvl)) { - let mut summary = parse_query_summary(&self.sql); + let mut summary = parse_query_summary(self.sql); let sql = if summary != self.sql { summary.push_str(" …"); - format!("\n\n{}\n", &self.sql) + format!("\n\n{}\n", self.sql) } else { String::new() }; diff --git a/sqlx-core/src/mssql/arguments.rs b/sqlx-core/src/mssql/arguments.rs index f8983ca2de..98e6de84be 100644 --- a/sqlx-core/src/mssql/arguments.rs +++ b/sqlx-core/src/mssql/arguments.rs @@ -86,7 +86,7 @@ impl MssqlArguments { // @p1 int, @p2 nvarchar(10), ... if !declarations.is_empty() { - declarations.push_str(","); + declarations.push(','); } declarations.push_str(name); diff --git a/sqlx-core/src/mssql/column.rs b/sqlx-core/src/mssql/column.rs index a6bdbe823c..c6db78cc78 100644 --- a/sqlx-core/src/mssql/column.rs +++ b/sqlx-core/src/mssql/column.rs @@ -33,7 +33,7 @@ impl Column for MssqlColumn { } fn name(&self) -> &str { - &*self.name + &self.name } fn type_info(&self) -> &MssqlTypeInfo { diff --git a/sqlx-core/src/mssql/connection/establish.rs b/sqlx-core/src/mssql/connection/establish.rs index 6e1c238274..d1fbb2175d 100644 --- a/sqlx-core/src/mssql/connection/establish.rs +++ b/sqlx-core/src/mssql/connection/establish.rs @@ -43,8 +43,7 @@ impl MssqlConnection { stream.setup_encryption().await?; } (Encrypt::Required, Encrypt::Off | Encrypt::NotSupported) => { - return Err(Error::Tls(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, + return Err(Error::Tls(Box::new(std::io::Error::other( "TLS encryption required but not supported by server", )))); } @@ -73,7 +72,7 @@ impl MssqlConnection { server_name: &options.server_name, client_interface_name: &options.client_interface_name, language: &options.language, - database: &*options.database, + database: &options.database, client_id: [0; 6], }; diff --git a/sqlx-core/src/mssql/connection/executor.rs b/sqlx-core/src/mssql/connection/executor.rs index 25e1480d05..87daceb708 100644 --- a/sqlx-core/src/mssql/connection/executor.rs +++ b/sqlx-core/src/mssql/connection/executor.rs @@ -71,12 +71,13 @@ impl MssqlConnection { impl<'c> Executor<'c> for &'c mut MssqlConnection { type Database = Mssql; - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, mut query: E, ) -> BoxStream<'e, Result, Error>> where 'c: 'e, + 'q: 'e, E: Execute<'q, Self::Database>, { let sql = query.sql(); @@ -135,13 +136,11 @@ impl<'c> Executor<'c> for &'c mut MssqlConnection { }) } - fn fetch_optional<'e, 'q: 'e, E: 'q>( - self, - query: E, - ) -> BoxFuture<'e, Result, Error>> + fn fetch_optional<'e, 'q, E>(self, query: E) -> BoxFuture<'e, Result, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { let mut s = self.fetch_many(query); @@ -156,13 +155,14 @@ impl<'c> Executor<'c> for &'c mut MssqlConnection { }) } - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, sql: &'q str, _parameters: &[MssqlTypeInfo], ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { let metadata = prepare(self, sql).await?; @@ -174,12 +174,13 @@ impl<'c> Executor<'c> for &'c mut MssqlConnection { }) } - fn describe<'e, 'q: 'e>( + fn describe<'e, 'q>( self, sql: &'q str, ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { let metadata = prepare(self, sql).await?; diff --git a/sqlx-core/src/mssql/connection/prepare.rs b/sqlx-core/src/mssql/connection/prepare.rs index 50d33983dd..db96af33cd 100644 --- a/sqlx-core/src/mssql/connection/prepare.rs +++ b/sqlx-core/src/mssql/connection/prepare.rs @@ -28,7 +28,7 @@ pub(crate) async fn prepare( for m in PARAMS_RE.captures_iter(sql) { if !params.is_empty() { - params.push_str(","); + params.push(','); } params.push_str(&m[0]); diff --git a/sqlx-core/src/mssql/connection/stream.rs b/sqlx-core/src/mssql/connection/stream.rs index 2d8d5b43f6..b19c26578d 100644 --- a/sqlx-core/src/mssql/connection/stream.rs +++ b/sqlx-core/src/mssql/connection/stream.rs @@ -142,7 +142,7 @@ impl MssqlStream { // TDS communicates in streams of packets that are themselves streams of messages pub(super) async fn recv_message(&mut self) -> Result { loop { - while self.response.as_ref().map_or(false, |r| !r.1.is_empty()) { + while self.response.as_ref().is_some_and(|r| !r.1.is_empty()) { let buf = if let Some((_, buf)) = self.response.as_mut() { buf } else { diff --git a/sqlx-core/src/mssql/connection/tls_prelogin_stream_wrapper.rs b/sqlx-core/src/mssql/connection/tls_prelogin_stream_wrapper.rs index 29c3ccf1da..7ab777129c 100644 --- a/sqlx-core/src/mssql/connection/tls_prelogin_stream_wrapper.rs +++ b/sqlx-core/src/mssql/connection/tls_prelogin_stream_wrapper.rs @@ -35,7 +35,6 @@ use std::task::{self, ready, Poll}; /// /// This allows us to use standard TLS libraries while still conforming to the TDS protocol /// requirements for the PRELOGIN phase. - const HEADER_BYTES: usize = 8; pub(crate) struct TlsPreloginWrapper { @@ -101,14 +100,14 @@ impl AsyncRead for TlsPreloginWrapper< let read = header_buf.filled().len(); if read == 0 { - return Poll::Ready(Ok(PollReadOut::default())); + return Poll::Ready(Ok(())); } inner.header_pos += read; } let header: PacketHeader = Decode::decode(Bytes::copy_from_slice(&inner.header_buf)) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; + .map_err(|err| io::Error::other(err))?; inner.read_remaining = usize::from(header.length) - HEADER_BYTES; @@ -122,7 +121,7 @@ impl AsyncRead for TlsPreloginWrapper< let max_read = std::cmp::min(inner.read_remaining, buf.remaining()); let mut limited_buf = buf.take(max_read); - let res = ready!(Pin::new(&mut inner.stream).poll_read(cx, &mut limited_buf))?; + ready!(Pin::new(&mut inner.stream).poll_read(cx, &mut limited_buf))?; let read = limited_buf.filled().len(); buf.advance(read); @@ -132,7 +131,7 @@ impl AsyncRead for TlsPreloginWrapper< inner.header_pos = 0; } - Poll::Ready(Ok(res)) + Poll::Ready(Ok(())) } #[cfg(feature = "_rt-async-std")] diff --git a/sqlx-core/src/mssql/options/parse.rs b/sqlx-core/src/mssql/options/parse.rs index dd5e6549c3..fb6d921c98 100644 --- a/sqlx-core/src/mssql/options/parse.rs +++ b/sqlx-core/src/mssql/options/parse.rs @@ -60,7 +60,7 @@ impl FromStr for MssqlConnectOptions { let username = url.username(); if !username.is_empty() { options = options.username( - &*percent_decode_str(username) + &percent_decode_str(username) .decode_utf8() .map_err(Error::config)?, ); @@ -68,7 +68,7 @@ impl FromStr for MssqlConnectOptions { if let Some(password) = url.password() { options = options.password( - &*percent_decode_str(password) + &percent_decode_str(password) .decode_utf8() .map_err(Error::config)?, ); @@ -82,7 +82,7 @@ impl FromStr for MssqlConnectOptions { for (key, value) in url.query_pairs() { match key.as_ref() { "instance" => { - options = options.instance(&*value); + options = options.instance(&value); } "encrypt" => { match value.to_lowercase().as_str() { @@ -104,7 +104,7 @@ impl FromStr for MssqlConnectOptions { options = options.trust_server_certificate(trust); } "hostname_in_certificate" => { - options = options.hostname_in_certificate(&*value); + options = options.hostname_in_certificate(&value); } "packet_size" => { let size = value.parse().map_err(Error::config)?; @@ -116,11 +116,11 @@ impl FromStr for MssqlConnectOptions { options = options.client_program_version(value.parse().map_err(Error::config)?) } "client_pid" => options = options.client_pid(value.parse().map_err(Error::config)?), - "hostname" => options = options.hostname(&*value), - "app_name" => options = options.app_name(&*value), - "server_name" => options = options.server_name(&*value), - "client_interface_name" => options = options.client_interface_name(&*value), - "language" => options = options.language(&*value), + "hostname" => options = options.hostname(&value), + "app_name" => options = options.app_name(&value), + "server_name" => options = options.server_name(&value), + "client_interface_name" => options = options.client_interface_name(&value), + "language" => options = options.language(&value), _ => { return Err(Error::config(MssqlInvalidOption(key.into()))); } diff --git a/sqlx-core/src/mssql/types/bigdecimal.rs b/sqlx-core/src/mssql/types/bigdecimal.rs index 5978456537..f559251660 100644 --- a/sqlx-core/src/mssql/types/bigdecimal.rs +++ b/sqlx-core/src/mssql/types/bigdecimal.rs @@ -40,7 +40,7 @@ impl Encode<'_, Mssql> for BigDecimal { fn produces(&self) -> Option { let mut info = >::type_info(); let (_biging, exponent) = self.as_bigint_and_exponent(); - info.0.scale = u8::try_from(exponent).unwrap_or(0); + info.0.scale = u8::try_from(exponent).expect("exponent should fit in u8"); Some(info) } diff --git a/sqlx-core/src/mssql/types/chrono.rs b/sqlx-core/src/mssql/types/chrono.rs index 89be7b73f9..27fb0a0e7f 100644 --- a/sqlx-core/src/mssql/types/chrono.rs +++ b/sqlx-core/src/mssql/types/chrono.rs @@ -162,7 +162,7 @@ where let mut encoded_offset: [u8; 2] = [0, 0]; LittleEndian::write_i16( &mut encoded_offset, - i16::try_from(seconds_from_utc / 60).unwrap_or(i16::MAX), + i16::try_from(seconds_from_utc / 60).expect("timezone minutes offset out of range"), ); buf.extend_from_slice(&encoded_offset); IsNull::No diff --git a/sqlx-core/src/mysql/arguments.rs b/sqlx-core/src/mysql/arguments.rs index 3d8dcce86f..f3d7b60299 100644 --- a/sqlx-core/src/mysql/arguments.rs +++ b/sqlx-core/src/mysql/arguments.rs @@ -23,7 +23,7 @@ impl MySqlArguments { self.null_bitmap.resize((index / 8) + 1, 0); if let IsNull::Yes = value.encode(&mut self.values) { - self.null_bitmap[index / 8] |= (1 << (index % 8)) as u8; + self.null_bitmap[index / 8] |= u8::try_from(1 << (index % 8)).unwrap_or(0); } } @@ -31,6 +31,11 @@ impl MySqlArguments { pub fn len(&self) -> usize { self.types.len() } + + #[doc(hidden)] + pub fn is_empty(&self) -> bool { + self.types.is_empty() + } } impl<'q> Arguments<'q> for MySqlArguments { diff --git a/sqlx-core/src/mysql/column.rs b/sqlx-core/src/mysql/column.rs index ecbe8ca774..ab40c47da8 100644 --- a/sqlx-core/src/mysql/column.rs +++ b/sqlx-core/src/mysql/column.rs @@ -24,7 +24,7 @@ impl Column for MySqlColumn { } fn name(&self) -> &str { - &*self.name + &self.name } fn type_info(&self) -> &MySqlTypeInfo { diff --git a/sqlx-core/src/mysql/connection/auth.rs b/sqlx-core/src/mysql/connection/auth.rs index 192e5be7fe..2e08f8fb46 100644 --- a/sqlx-core/src/mysql/connection/auth.rs +++ b/sqlx-core/src/mysql/connection/auth.rs @@ -19,12 +19,12 @@ impl AuthPlugin { ) -> Result, Error> { match self { // https://mariadb.com/kb/en/caching_sha2_password-authentication-plugin/ - AuthPlugin::CachingSha2Password => Ok(scramble_sha256(password, nonce)), + AuthPlugin::CachingSha2 => Ok(scramble_sha256(password, nonce)), - AuthPlugin::MySqlNativePassword => Ok(scramble_sha1(password, nonce)), + AuthPlugin::MySqlNative => Ok(scramble_sha1(password, nonce)), // https://mariadb.com/kb/en/sha256_password-plugin/ - AuthPlugin::Sha256Password => encrypt_rsa(stream, 0x01, password, nonce).await, + AuthPlugin::Sha256 => encrypt_rsa(stream, 0x01, password, nonce).await, } } @@ -36,7 +36,7 @@ impl AuthPlugin { nonce: &Chain, ) -> Result { match self { - AuthPlugin::CachingSha2Password if packet[0] == 0x01 => { + AuthPlugin::CachingSha2 if packet[0] == 0x01 => { match packet[1] { // AUTH_OK 0x03 => Ok(true), @@ -76,7 +76,7 @@ fn scramble_sha1(password: &str, nonce: &Chain) -> Vec { let mut pw_hash = ctx.finalize_reset(); - ctx.update(&pw_hash); + ctx.update(pw_hash); let pw_hash_hash = ctx.finalize_reset(); @@ -100,7 +100,7 @@ fn scramble_sha256(password: &str, nonce: &Chain) -> Vec { let mut pw_hash = ctx.finalize_reset(); - ctx.update(&pw_hash); + ctx.update(pw_hash); let pw_hash_hash = ctx.finalize_reset(); @@ -141,10 +141,10 @@ async fn encrypt_rsa<'s>( let (a, b) = (nonce.first_ref(), nonce.last_ref()); let mut nonce = Vec::with_capacity(a.len() + b.len()); - nonce.extend_from_slice(&*a); - nonce.extend_from_slice(&*b); + nonce.extend_from_slice(a); + nonce.extend_from_slice(b); - xor_eq(&mut pass, &*nonce); + xor_eq(&mut pass, &nonce); // client sends an RSA encrypted password let pkey = parse_rsa_pub_key(rsa_pub_key)?; @@ -179,5 +179,5 @@ fn parse_rsa_pub_key(key: &[u8]) -> Result { // we are receiving a PKCS#8 RSA Public Key at all // times from MySQL - RsaPublicKey::from_public_key_pem(&pem).map_err(Error::protocol) + RsaPublicKey::from_public_key_pem(pem).map_err(Error::protocol) } diff --git a/sqlx-core/src/mysql/connection/executor.rs b/sqlx-core/src/mysql/connection/executor.rs index 8d73794f57..7d7a5c6fbd 100644 --- a/sqlx-core/src/mysql/connection/executor.rs +++ b/sqlx-core/src/mysql/connection/executor.rs @@ -25,7 +25,7 @@ use futures_util::{pin_mut, TryStreamExt}; use std::{borrow::Cow, sync::Arc}; impl MySqlConnection { - async fn get_or_prepare<'c>( + async fn get_or_prepare( &mut self, sql: &str, persistent: bool, @@ -155,7 +155,7 @@ impl MySqlConnection { // otherwise, this first packet is the start of the result-set metadata, *self.stream.waiting.front_mut().unwrap() = Waiting::Row; - let num_columns = packet.get_uint_lenenc() as usize; // column count + let num_columns = usize::try_from(packet.get_uint_lenenc()).expect("column count should fit in usize"); // column count if needs_metadata { column_names = Arc::new(recv_result_metadata(&mut self.stream, num_columns, Arc::make_mut(&mut columns)).await?); @@ -213,12 +213,13 @@ impl MySqlConnection { impl<'c> Executor<'c> for &'c mut MySqlConnection { type Database = MySql; - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, mut query: E, ) -> BoxStream<'e, Result, Error>> where 'c: 'e, + 'q: 'e, E: Execute<'q, Self::Database>, { let sql = query.sql(); @@ -237,13 +238,11 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection { }) } - fn fetch_optional<'e, 'q: 'e, E: 'q>( - self, - query: E, - ) -> BoxFuture<'e, Result, Error>> + fn fetch_optional<'e, 'q, E>(self, query: E) -> BoxFuture<'e, Result, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { let mut s = self.fetch_many(query); @@ -258,13 +257,14 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection { }) } - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, sql: &'q str, _parameters: &'e [MySqlTypeInfo], ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { self.stream.wait_until_ready().await?; @@ -280,16 +280,17 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection { } #[doc(hidden)] - fn describe<'e, 'q: 'e>(self, sql: &'q str) -> BoxFuture<'e, Result, Error>> + fn describe<'e, 'q>(self, sql: &'q str) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { self.stream.wait_until_ready().await?; let (_, metadata) = self.get_or_prepare(sql, false).await?; - let columns = (&*metadata.columns).clone(); + let columns = (*metadata.columns).clone(); let nullable = columns .iter() @@ -335,7 +336,7 @@ fn recv_next_result_column(def: &ColumnDefinition, ordinal: usize) -> Result UStr::new(name), }; - let type_info = MySqlTypeInfo::from_column(&def); + let type_info = MySqlTypeInfo::from_column(def); Ok(MySqlColumn { name, diff --git a/sqlx-core/src/mysql/connection/stream.rs b/sqlx-core/src/mysql/connection/stream.rs index dd9a1235b8..96910579e7 100644 --- a/sqlx-core/src/mysql/connection/stream.rs +++ b/sqlx-core/src/mysql/connection/stream.rs @@ -138,7 +138,10 @@ impl MySqlStream { let mut header: Bytes = self.stream.read(4).await?; - let packet_size = header.get_uint_le(3) as usize; + let packet_size: usize = header + .get_uint_le(3) + .try_into() + .expect("packet length should fit in usize"); let sequence_id = header.get_u8(); self.sequence_id = sequence_id.wrapping_add(1); @@ -149,7 +152,7 @@ impl MySqlStream { // TODO: packet joining if payload - .get(0) + .first() .ok_or(err_protocol!("Packet empty"))? .eq(&0xff) { diff --git a/sqlx-core/src/mysql/io/buf.rs b/sqlx-core/src/mysql/io/buf.rs index 98bb3407d9..9840f98287 100644 --- a/sqlx-core/src/mysql/io/buf.rs +++ b/sqlx-core/src/mysql/io/buf.rs @@ -31,11 +31,11 @@ impl MySqlBufExt for Bytes { fn get_str_lenenc(&mut self) -> Result { let size = self.get_uint_lenenc(); - self.get_str(size as usize) + self.get_str(usize::try_from(size).expect("string length should fit in usize")) } fn get_bytes_lenenc(&mut self) -> Bytes { let size = self.get_uint_lenenc(); - self.split_to(size as usize) + self.split_to(usize::try_from(size).expect("split size should fit in usize")) } } diff --git a/sqlx-core/src/mysql/io/buf_mut.rs b/sqlx-core/src/mysql/io/buf_mut.rs index 95bf04f319..d74b46f889 100644 --- a/sqlx-core/src/mysql/io/buf_mut.rs +++ b/sqlx-core/src/mysql/io/buf_mut.rs @@ -14,13 +14,17 @@ impl MySqlBufMutExt for Vec { // https://mariadb.com/kb/en/library/protocol-data-types/#length-encoded-integers if v < 251 { - self.push(v as u8); + self.push(u8::try_from(v).expect("value < 251 should fit in u8")); } else if v < 0x1_00_00 { self.push(0xfc); - self.extend(&(v as u16).to_le_bytes()); + self.extend(&u16::try_from(v).expect("value < 0x1_00_00").to_le_bytes()); } else if v < 0x1_00_00_00 { self.push(0xfd); - self.extend(&(v as u32).to_le_bytes()[..3]); + self.extend( + &u32::try_from(v) + .expect("value < 0x1_00_00_00") + .to_le_bytes()[..3], + ); } else { self.push(0xfe); self.extend(&v.to_le_bytes()); diff --git a/sqlx-core/src/mysql/migrate.rs b/sqlx-core/src/mysql/migrate.rs index 3a23616950..31b6ff7bff 100644 --- a/sqlx-core/src/mysql/migrate.rs +++ b/sqlx-core/src/mysql/migrate.rs @@ -192,11 +192,11 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( .map_err(MigrateError::AccessMigrationMetadata)?; if let Some(checksum) = checksum { - return if checksum == &*migration.checksum { + if checksum == *migration.checksum { Ok(()) } else { Err(MigrateError::VersionMismatch(migration.version)) - }; + } } else { Err(MigrateError::VersionMissing(migration.version)) } @@ -264,7 +264,7 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( WHERE version = ? "#, ) - .bind(elapsed.as_nanos() as i64) + .bind(i64::try_from(elapsed.as_nanos()).expect("elapsed nanos should fit in i64")) .bind(migration.version) .execute(self) .await?; diff --git a/sqlx-core/src/mysql/options/parse.rs b/sqlx-core/src/mysql/options/parse.rs index b1a2e98485..4854642402 100644 --- a/sqlx-core/src/mysql/options/parse.rs +++ b/sqlx-core/src/mysql/options/parse.rs @@ -22,7 +22,7 @@ impl FromStr for MySqlConnectOptions { let username = url.username(); if !username.is_empty() { options = options.username( - &*percent_decode_str(username) + &percent_decode_str(username) .decode_utf8() .map_err(Error::config)?, ); @@ -30,7 +30,7 @@ impl FromStr for MySqlConnectOptions { if let Some(password) = url.password() { options = options.password( - &*percent_decode_str(password) + &percent_decode_str(password) .decode_utf8() .map_err(Error::config)?, ); @@ -52,11 +52,11 @@ impl FromStr for MySqlConnectOptions { } "charset" => { - options = options.charset(&*value); + options = options.charset(&value); } "collation" => { - options = options.collation(&*value); + options = options.collation(&value); } "statement-cache-capacity" => { diff --git a/sqlx-core/src/mysql/options/ssl_mode.rs b/sqlx-core/src/mysql/options/ssl_mode.rs index a9abf08441..9256660795 100644 --- a/sqlx-core/src/mysql/options/ssl_mode.rs +++ b/sqlx-core/src/mysql/options/ssl_mode.rs @@ -4,7 +4,7 @@ use std::str::FromStr; /// Options for controlling the desired security state of the connection to the MySQL server. /// /// It is used by the [`ssl_mode`](super::MySqlConnectOptions::ssl_mode) method. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub enum MySqlSslMode { /// Establish an unencrypted connection. Disabled, @@ -13,6 +13,7 @@ pub enum MySqlSslMode { /// back to an unencrypted connection if an encrypted connection cannot be established. /// /// This is the default if `ssl_mode` is not specified. + #[default] Preferred, /// Establish an encrypted connection if the server supports encrypted connections. @@ -30,11 +31,7 @@ pub enum MySqlSslMode { VerifyIdentity, } -impl Default for MySqlSslMode { - fn default() -> Self { - MySqlSslMode::Preferred - } -} +// Default is derived above impl FromStr for MySqlSslMode { type Err = Error; diff --git a/sqlx-core/src/mysql/protocol/auth.rs b/sqlx-core/src/mysql/protocol/auth.rs index 261a8817ab..60e4add319 100644 --- a/sqlx-core/src/mysql/protocol/auth.rs +++ b/sqlx-core/src/mysql/protocol/auth.rs @@ -4,17 +4,17 @@ use crate::error::Error; #[derive(Debug, Copy, Clone)] pub enum AuthPlugin { - MySqlNativePassword, - CachingSha2Password, - Sha256Password, + MySqlNative, + CachingSha2, + Sha256, } impl AuthPlugin { pub(crate) fn name(self) -> &'static str { match self { - AuthPlugin::MySqlNativePassword => "mysql_native_password", - AuthPlugin::CachingSha2Password => "caching_sha2_password", - AuthPlugin::Sha256Password => "sha256_password", + AuthPlugin::MySqlNative => "mysql_native_password", + AuthPlugin::CachingSha2 => "caching_sha2_password", + AuthPlugin::Sha256 => "sha256_password", } } } @@ -24,9 +24,9 @@ impl FromStr for AuthPlugin { fn from_str(s: &str) -> Result { match s { - "mysql_native_password" => Ok(AuthPlugin::MySqlNativePassword), - "caching_sha2_password" => Ok(AuthPlugin::CachingSha2Password), - "sha256_password" => Ok(AuthPlugin::Sha256Password), + "mysql_native_password" => Ok(AuthPlugin::MySqlNative), + "caching_sha2_password" => Ok(AuthPlugin::CachingSha2), + "sha256_password" => Ok(AuthPlugin::Sha256), _ => Err(err_protocol!("unknown authentication plugin: {}", s)), } diff --git a/sqlx-core/src/mysql/protocol/connect/handshake.rs b/sqlx-core/src/mysql/protocol/connect/handshake.rs index e0a4497bbd..a014a8b342 100644 --- a/sqlx-core/src/mysql/protocol/connect/handshake.rs +++ b/sqlx-core/src/mysql/protocol/connect/handshake.rs @@ -61,7 +61,8 @@ impl Decode<'_> for Handshake { } let auth_plugin_data_2 = if capabilities.contains(Capabilities::SECURE_CONNECTION) { - let len = ((auth_plugin_data_len as isize) - 9).max(12) as usize; + let len = usize::try_from(core::cmp::max((auth_plugin_data_len as isize) - 9, 12)) + .expect("auth plugin data length should be non-negative"); let v = buf.get_bytes(len); buf.advance(1); // NUL-terminator @@ -132,10 +133,7 @@ fn test_decode_handshake_mysql_8_0_18() { assert_eq!(p.server_default_collation, 255); assert!(p.status.contains(Status::SERVER_STATUS_AUTOCOMMIT)); - assert!(matches!( - p.auth_plugin, - Some(AuthPlugin::CachingSha2Password) - )); + assert!(matches!(p.auth_plugin, Some(AuthPlugin::CachingSha2))); assert_eq!( &*p.auth_plugin_data.into_iter().collect::>(), @@ -185,10 +183,7 @@ fn test_decode_handshake_mariadb_10_4_7() { assert_eq!(p.server_default_collation, 8); assert!(p.status.contains(Status::SERVER_STATUS_AUTOCOMMIT)); - assert!(matches!( - p.auth_plugin, - Some(AuthPlugin::MySqlNativePassword) - )); + assert!(matches!(p.auth_plugin, Some(AuthPlugin::MySqlNative))); assert_eq!( &*p.auth_plugin_data.into_iter().collect::>(), diff --git a/sqlx-core/src/mysql/protocol/connect/handshake_response.rs b/sqlx-core/src/mysql/protocol/connect/handshake_response.rs index 22933006e3..5a85f24bf2 100644 --- a/sqlx-core/src/mysql/protocol/connect/handshake_response.rs +++ b/sqlx-core/src/mysql/protocol/connect/handshake_response.rs @@ -48,7 +48,7 @@ impl Encode<'_, Capabilities> for HandshakeResponse<'_> { } else if capabilities.contains(Capabilities::SECURE_CONNECTION) { let response = self.auth_response.unwrap_or_default(); - buf.push(response.len() as u8); + buf.push(u8::try_from(response.len()).expect("auth response length should fit in u8")); buf.extend(response); } else { buf.push(0); diff --git a/sqlx-core/src/mysql/protocol/connect/ssl_request.rs b/sqlx-core/src/mysql/protocol/connect/ssl_request.rs index 5f0c2d8946..a7689b4414 100644 --- a/sqlx-core/src/mysql/protocol/connect/ssl_request.rs +++ b/sqlx-core/src/mysql/protocol/connect/ssl_request.rs @@ -12,7 +12,11 @@ pub struct SslRequest { impl Encode<'_, Capabilities> for SslRequest { fn encode_with(&self, buf: &mut Vec, capabilities: Capabilities) { - buf.extend(&(capabilities.bits() as u32).to_le_bytes()); + buf.extend( + &u32::try_from(capabilities.bits()) + .expect("capabilities bits should fit in u32") + .to_le_bytes(), + ); buf.extend(&self.max_packet_size.to_le_bytes()); buf.push(self.collation); @@ -24,7 +28,11 @@ impl Encode<'_, Capabilities> for SslRequest { buf.extend(&[0_u8; 4]); } else { // extended client capabilities (MariaDB-specified): int<4> - buf.extend(&((capabilities.bits() >> 32) as u32).to_le_bytes()); + buf.extend( + &u32::try_from(capabilities.bits() >> 32) + .expect("capabilities hi bits should fit in u32") + .to_le_bytes(), + ); } } } diff --git a/sqlx-core/src/mysql/protocol/packet.rs b/sqlx-core/src/mysql/protocol/packet.rs index 8c49fcc335..9bf68b2d22 100644 --- a/sqlx-core/src/mysql/protocol/packet.rs +++ b/sqlx-core/src/mysql/protocol/packet.rs @@ -34,7 +34,11 @@ where // FIXME: Support larger packets assert!(len < 0xFF_FF_FF); - header[..4].copy_from_slice(&(len as u32).to_le_bytes()); + header[..4].copy_from_slice( + &u32::try_from(len) + .expect("packet length should fit in u32") + .to_le_bytes(), + ); header[3] = *sequence_id; *sequence_id = sequence_id.wrapping_add(1); diff --git a/sqlx-core/src/mysql/protocol/response/eof.rs b/sqlx-core/src/mysql/protocol/response/eof.rs index 756c370a62..e268c8fc3f 100644 --- a/sqlx-core/src/mysql/protocol/response/eof.rs +++ b/sqlx-core/src/mysql/protocol/response/eof.rs @@ -13,6 +13,7 @@ use crate::mysql::protocol::Capabilities; /// prior MySQL versions. #[derive(Debug)] pub struct EofPacket { + #[allow(dead_code)] pub warnings: u16, pub status: Status, } diff --git a/sqlx-core/src/mysql/protocol/response/err.rs b/sqlx-core/src/mysql/protocol/response/err.rs index e365a8e0a7..3933621ab9 100644 --- a/sqlx-core/src/mysql/protocol/response/err.rs +++ b/sqlx-core/src/mysql/protocol/response/err.rs @@ -30,7 +30,7 @@ impl Decode<'_, Capabilities> for ErrPacket { if capabilities.contains(Capabilities::PROTOCOL_41) { // If the next byte is '#' then we have a SQL STATE - if buf.get(0) == Some(&0x23) { + if buf.first() == Some(&0x23) { buf.advance(1); sql_state = Some(buf.get_str(5)?); } diff --git a/sqlx-core/src/mysql/protocol/row.rs b/sqlx-core/src/mysql/protocol/row.rs index f027dada5f..ee52ed61ec 100644 --- a/sqlx-core/src/mysql/protocol/row.rs +++ b/sqlx-core/src/mysql/protocol/row.rs @@ -12,6 +12,6 @@ impl Row { pub(crate) fn get(&self, index: usize) -> Option<&[u8]> { self.values[index] .as_ref() - .map(|col| &self.storage[(col.start as usize)..(col.end as usize)]) + .map(|col| &self.storage[(col.start)..(col.end)]) } } diff --git a/sqlx-core/src/mysql/protocol/statement/row.rs b/sqlx-core/src/mysql/protocol/statement/row.rs index b3760de9a3..57adbbca0c 100644 --- a/sqlx-core/src/mysql/protocol/statement/row.rs +++ b/sqlx-core/src/mysql/protocol/statement/row.rs @@ -34,8 +34,9 @@ impl<'de> Decode<'de, &'de [MySqlColumn]> for BinaryRow { for (column_idx, column) in columns.iter().enumerate() { // NOTE: the column index starts at the 3rd bit let column_null_idx = column_idx + 2; - let is_null = - null_bitmap[column_null_idx / 8] & (1 << (column_null_idx % 8) as u8) != 0; + let is_null = null_bitmap[column_null_idx / 8] + & (1 << u8::try_from(column_null_idx % 8).expect("bit index < 8")) + != 0; if is_null { values.push(None); @@ -59,7 +60,8 @@ impl<'de> Decode<'de, &'de [MySqlColumn]> for BinaryRow { | ColumnType::Bit | ColumnType::Decimal | ColumnType::Json - | ColumnType::NewDecimal => buf.get_uint_lenenc() as usize, + | ColumnType::NewDecimal => usize::try_from(buf.get_uint_lenenc()) + .expect("length-encoded size should fit in usize"), ColumnType::LongLong => 8, ColumnType::Long | ColumnType::Int24 => 4, diff --git a/sqlx-core/src/mysql/protocol/text/row.rs b/sqlx-core/src/mysql/protocol/text/row.rs index 17d32f10b7..3bb307692c 100644 --- a/sqlx-core/src/mysql/protocol/text/row.rs +++ b/sqlx-core/src/mysql/protocol/text/row.rs @@ -22,7 +22,8 @@ impl<'de> Decode<'de, &'de [MySqlColumn]> for TextRow { values.push(None); buf.advance(1); } else { - let size = buf.get_uint_lenenc() as usize; + let size = usize::try_from(buf.get_uint_lenenc()) + .expect("length-encoded size should fit in usize"); let offset = offset - buf.len(); values.push(Some(offset..(offset + size))); diff --git a/sqlx-core/src/mysql/query_result.rs b/sqlx-core/src/mysql/query_result.rs index 41dcdbc1bb..6de48332b8 100644 --- a/sqlx-core/src/mysql/query_result.rs +++ b/sqlx-core/src/mysql/query_result.rs @@ -30,7 +30,7 @@ impl From for crate::any::AnyQueryResult { fn from(done: MySqlQueryResult) -> Self { crate::any::AnyQueryResult { rows_affected: done.rows_affected, - last_insert_id: Some(done.last_insert_id as i64), + last_insert_id: i64::try_from(done.last_insert_id).ok(), } } } diff --git a/sqlx-core/src/mysql/row.rs b/sqlx-core/src/mysql/row.rs index f910ded68d..f1c083a4e8 100644 --- a/sqlx-core/src/mysql/row.rs +++ b/sqlx-core/src/mysql/row.rs @@ -46,7 +46,7 @@ impl ColumnIndex for &'_ str { row.column_names .get(*self) .ok_or_else(|| Error::ColumnNotFound((*self).into())) - .map(|v| *v) + .copied() } } diff --git a/sqlx-core/src/mysql/statement.rs b/sqlx-core/src/mysql/statement.rs index b6de92fa49..16925447a0 100644 --- a/sqlx-core/src/mysql/statement.rs +++ b/sqlx-core/src/mysql/statement.rs @@ -54,7 +54,7 @@ impl ColumnIndex> for &'_ str { .column_names .get(*self) .ok_or_else(|| Error::ColumnNotFound((*self).into())) - .map(|v| *v) + .copied() } } diff --git a/sqlx-core/src/mysql/testing/mod.rs b/sqlx-core/src/mysql/testing/mod.rs index 5f476e74a2..6e65988216 100644 --- a/sqlx-core/src/mysql/testing/mod.rs +++ b/sqlx-core/src/mysql/testing/mod.rs @@ -45,7 +45,7 @@ impl TestSupport for MySql { .await?; query("delete from _sqlx_test_databases where db_id = ?") - .bind(&db_id) + .bind(db_id) .execute(&mut conn) .await?; @@ -127,7 +127,7 @@ async fn test_context(args: &TestArgs) -> Result, Error> { } query("insert into _sqlx_test_databases(test_path) values (?)") - .bind(&args.test_path) + .bind(args.test_path) .execute(&mut conn) .await?; @@ -199,7 +199,7 @@ async fn do_cleanup(conn: &mut MySqlConnection) -> Result { separated.push_bind(db_id); } - drop(separated); + let _ = separated; query.push(")").build().execute(&mut *conn).await?; diff --git a/sqlx-core/src/mysql/transaction.rs b/sqlx-core/src/mysql/transaction.rs index 97cb121d0e..8334eb246f 100644 --- a/sqlx-core/src/mysql/transaction.rs +++ b/sqlx-core/src/mysql/transaction.rs @@ -60,7 +60,7 @@ impl TransactionManager for MySqlTransactionManager { conn.stream.waiting.push_back(Waiting::Result); conn.stream.sequence_id = 0; conn.stream - .write_packet(Query(&*rollback_ansi_transaction_sql(depth))); + .write_packet(Query(&rollback_ansi_transaction_sql(depth))); conn.transaction_depth = depth - 1; } diff --git a/sqlx-core/src/mysql/types/float.rs b/sqlx-core/src/mysql/types/float.rs index b6d723142d..6f15f9955c 100644 --- a/sqlx-core/src/mysql/types/float.rs +++ b/sqlx-core/src/mysql/types/float.rs @@ -51,7 +51,12 @@ impl Encode<'_, MySql> for f64 { impl Decode<'_, MySql> for f32 { fn decode(value: MySqlValueRef<'_>) -> Result { let as_f64 = >::decode(value)?; - Ok(as_f64 as f32) + // Clamp to f32 range, then cast; failing to parse into range is a bug upstream. + let clamped = as_f64.clamp(f32::MIN as f64, f32::MAX as f64); + #[allow(clippy::cast_possible_truncation)] + { + Ok(clamped as f32) + } } } @@ -72,8 +77,7 @@ impl Decode<'_, MySql> for f64 { } _ => { let str_val = value.as_str()?; - let parsed = str_val.parse()?; - parsed + str_val.parse()? } }) } diff --git a/sqlx-core/src/mysql/types/json.rs b/sqlx-core/src/mysql/types/json.rs index 86dce1ad8c..cfa32aad21 100644 --- a/sqlx-core/src/mysql/types/json.rs +++ b/sqlx-core/src/mysql/types/json.rs @@ -41,7 +41,7 @@ where fn decode(value: MySqlValueRef<'r>) -> Result { let string_value = <&str as Decode>::decode(value)?; - serde_json::from_str(&string_value) + serde_json::from_str(string_value) .map(Json) .map_err(Into::into) } diff --git a/sqlx-core/src/mysql/value.rs b/sqlx-core/src/mysql/value.rs index f926f6d767..2d4e614e1f 100644 --- a/sqlx-core/src/mysql/value.rs +++ b/sqlx-core/src/mysql/value.rs @@ -93,7 +93,7 @@ impl<'r> ValueRef<'r> for MySqlValueRef<'r> { #[inline] fn is_null(&self) -> bool { - is_null(self.value.as_deref(), &self.type_info) + is_null(self.value, &self.type_info) } } @@ -125,7 +125,7 @@ fn is_null(value: Option<&[u8]>, ty: &MySqlTypeInfo) -> bool { if matches!( ty.r#type, ColumnType::Date | ColumnType::Timestamp | ColumnType::Datetime - ) && (value.get(0) == Some(&0) + ) && (value.first() == Some(&0) || value == b"0000-00-00" || value == b"0000-00-00 00:00:00") { diff --git a/sqlx-core/src/net/socket.rs b/sqlx-core/src/net/socket.rs index 622a1a22ce..cacd5d2e8b 100644 --- a/sqlx-core/src/net/socket.rs +++ b/sqlx-core/src/net/socket.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use std::io; use std::net::SocketAddr; use std::path::Path; @@ -31,6 +29,7 @@ impl Socket { .map(Socket::Unix) } + #[allow(dead_code)] pub fn local_addr(&self) -> Option { match self { Self::Tcp(tcp) => tcp.local_addr().ok(), diff --git a/sqlx-core/src/net/tls/mod.rs b/sqlx-core/src/net/tls/mod.rs index cea69374f2..3d6ad610ee 100644 --- a/sqlx-core/src/net/tls/mod.rs +++ b/sqlx-core/src/net/tls/mod.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use std::io; use std::ops::{Deref, DerefMut}; use std::path::PathBuf; @@ -98,7 +96,7 @@ where S: AsyncRead + AsyncWrite + Unpin, { Raw(S), - Tls(TlsStream), + Tls(Box>), Upgrading, } @@ -115,17 +113,19 @@ where let host = config.hostname; let connector = configure_tls_connector(config).await?; - let stream = match replace(self, MaybeTlsStream::Upgrading) { + let stream: S = match replace(self, MaybeTlsStream::Upgrading) { MaybeTlsStream::Raw(stream) => stream, MaybeTlsStream::Tls(_) => { // ignore upgrade, we are already a TLS connection + *self = MaybeTlsStream::Upgrading; return Ok(()); } MaybeTlsStream::Upgrading => { // we previously failed to upgrade and now hold no connection // this should only happen from an internal misuse of this method + *self = MaybeTlsStream::Upgrading; return Err(Error::Io(io::ErrorKind::ConnectionAborted.into())); } }; @@ -135,11 +135,11 @@ where .map_err(|err| Error::Tls(err.into()))? .to_owned(); - *self = MaybeTlsStream::Tls(connector.connect(host, stream).await?); - + *self = MaybeTlsStream::Tls(Box::new(connector.connect(host, stream).await?)); Ok(()) } + #[allow(dead_code)] pub fn downgrade(&mut self) -> Result<(), Error> { match replace(self, MaybeTlsStream::Upgrading) { MaybeTlsStream::Tls(stream) => { @@ -147,24 +147,22 @@ where { let raw = stream.into_inner().0; *self = MaybeTlsStream::Raw(raw); - return Ok(()); + Ok(()) } #[cfg(feature = "_tls-native-tls")] { let _ = stream; // Use the variable to avoid warning - return Err(Error::tls("No way to downgrade a native-tls stream, use rustls instead, or never disable tls")); + Err(Error::tls("No way to downgrade a native-tls stream, use rustls instead, or never disable tls")) } } MaybeTlsStream::Raw(stream) => { *self = MaybeTlsStream::Raw(stream); - return Ok(()); + Ok(()) } - MaybeTlsStream::Upgrading => { - return Err(Error::Io(io::ErrorKind::ConnectionAborted.into())); - } + MaybeTlsStream::Upgrading => Err(Error::Io(io::ErrorKind::ConnectionAborted.into())), } } } diff --git a/sqlx-core/src/net/tls/rustls.rs b/sqlx-core/src/net/tls/rustls.rs index b97a06bc2d..cda639ed27 100644 --- a/sqlx-core/src/net/tls/rustls.rs +++ b/sqlx-core/src/net/tls/rustls.rs @@ -72,14 +72,14 @@ pub async fn configure_tls_connector( .with_custom_certificate_verifier(Arc::new(DummyTlsVerifier)) } else { let mut cert_store = RootCertStore { - roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(), + roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), }; if let Some(ca) = tls_config.root_cert_path { let path_description = ca.to_string(); let data = ca.data().await.map_err(|e| RustlsError::ParsePemCert { file_description: path_description.clone(), - source: std::io::Error::new(std::io::ErrorKind::Other, e), + source: std::io::Error::other(e), })?; let mut cursor = Cursor::new(data); @@ -116,13 +116,13 @@ pub async fn configure_tls_connector( let cert_chain = certs_from_pem(cert_path.data().await.map_err(|e| { RustlsError::ParseClientCert { file_description: cert_file_desc.clone(), - source: std::io::Error::new(std::io::ErrorKind::Other, e), + source: std::io::Error::other(e), } })?)?; let key_der = private_key_from_pem(key_path.data().await.map_err(|e| { RustlsError::ParseClientKey { file_description: key_file_desc.clone(), - source: std::io::Error::new(std::io::ErrorKind::Other, e), + source: std::io::Error::other(e), } })?)?; config diff --git a/sqlx-core/src/pool/executor.rs b/sqlx-core/src/pool/executor.rs index 97c8905bab..7d796156b0 100644 --- a/sqlx-core/src/pool/executor.rs +++ b/sqlx-core/src/pool/executor.rs @@ -15,12 +15,13 @@ where { type Database = DB; - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, query: E, ) -> BoxStream<'e, Result, Error>> where - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { let pool = self.clone(); @@ -36,12 +37,10 @@ where }) } - fn fetch_optional<'e, 'q: 'e, E: 'q>( - self, - query: E, - ) -> BoxFuture<'e, Result, Error>> + fn fetch_optional<'e, 'q, E>(self, query: E) -> BoxFuture<'e, Result, Error>> where - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { let pool = self.clone(); @@ -77,7 +76,7 @@ macro_rules! impl_executor_for_pool_connection { type Database = $DB; #[inline] - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, query: E, ) -> futures_core::stream::BoxStream< @@ -89,25 +88,27 @@ macro_rules! impl_executor_for_pool_connection { > where 'c: 'e, - E: crate::executor::Execute<'q, $DB>, + 'q: 'e, + E: crate::executor::Execute<'q, $DB> + 'e, { (**self).fetch_many(query) } #[inline] - fn fetch_optional<'e, 'q: 'e, E: 'q>( + fn fetch_optional<'e, 'q, E>( self, query: E, ) -> futures_core::future::BoxFuture<'e, Result, crate::error::Error>> where 'c: 'e, - E: crate::executor::Execute<'q, $DB>, + 'q: 'e, + E: crate::executor::Execute<'q, $DB> + 'e, { (**self).fetch_optional(query) } #[inline] - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, sql: &'q str, parameters: &'e [<$DB as crate::database::Database>::TypeInfo], @@ -117,13 +118,14 @@ macro_rules! impl_executor_for_pool_connection { > where 'c: 'e, + 'q: 'e, { (**self).prepare_with(sql, parameters) } #[doc(hidden)] #[inline] - fn describe<'e, 'q: 'e>( + fn describe<'e, 'q>( self, sql: &'q str, ) -> futures_core::future::BoxFuture< @@ -132,6 +134,7 @@ macro_rules! impl_executor_for_pool_connection { > where 'c: 'e, + 'q: 'e, { (**self).describe(sql) } diff --git a/sqlx-core/src/pool/inner.rs b/sqlx-core/src/pool/inner.rs index 398ece1f2a..8491688d6d 100644 --- a/sqlx-core/src/pool/inner.rs +++ b/sqlx-core/src/pool/inner.rs @@ -142,7 +142,10 @@ impl PoolInner { if parent_close_event.as_mut().poll(cx).is_ready() { // Propagate the parent's close event to the child. - let _ = self.close(); + let pool = Arc::clone(self); + sqlx_rt::spawn(async move { + let _ = pool.close().await; + }); return Poll::Ready(Err(Error::PoolClosed)); } @@ -197,7 +200,7 @@ impl PoolInner { let Floating { inner: idle, guard } = floating.into_idle(); - if !self.idle_conns.push(idle).is_ok() { + if self.idle_conns.push(idle).is_err() { panic!("BUG: connection queue overflow in release()"); } @@ -287,10 +290,10 @@ impl PoolInner { } let mut backoff = Duration::from_millis(10); - let max_backoff = deadline_as_timeout::(deadline)? / 5; + let max_backoff = deadline_as_timeout(deadline)? / 5; loop { - let timeout = deadline_as_timeout::(deadline)?; + let timeout = deadline_as_timeout(deadline)?; // result here is `Result, TimeoutError>` // if this block does not return, sleep for the backoff timeout and try again @@ -403,14 +406,14 @@ impl Drop for PoolInner { fn is_beyond_max_lifetime(live: &Live, options: &PoolOptions) -> bool { options .max_lifetime - .map_or(false, |max| live.created_at.elapsed() > max) + .is_some_and(|max| live.created_at.elapsed() > max) } /// Returns `true` if the connection has exceeded `options.idle_timeout` if set, `false` otherwise. fn is_beyond_idle_timeout(idle: &Idle, options: &PoolOptions) -> bool { options .idle_timeout - .map_or(false, |timeout| idle.idle_since.elapsed() > timeout) + .is_some_and(|timeout| idle.idle_since.elapsed() > timeout) } async fn check_idle_conn( @@ -458,7 +461,7 @@ async fn check_idle_conn( } fn spawn_maintenance_tasks(pool: &Arc>) { - let pool = Arc::clone(&pool); + let pool = Arc::clone(pool); let period = match (pool.options.max_lifetime, pool.options.idle_timeout) { (Some(it), None) | (None, Some(it)) => it, diff --git a/sqlx-core/src/pool/mod.rs b/sqlx-core/src/pool/mod.rs index 576f2ea3b5..b398349019 100644 --- a/sqlx-core/src/pool/mod.rs +++ b/sqlx-core/src/pool/mod.rs @@ -265,7 +265,10 @@ impl Pool { /// For production applications, you'll likely want to make at least few tweaks. /// /// See [`PoolOptions::new()`] for details. - pub async fn connect(url: &str) -> Result { + pub async fn connect(url: &str) -> Result + where + ::Options: std::str::FromStr, + { PoolOptions::::new().connect(url).await } @@ -298,7 +301,10 @@ impl Pool { /// For production applications, you'll likely want to make at least few tweaks. /// /// See [`PoolOptions::new()`] for details. - pub fn connect_lazy(url: &str) -> Result { + pub fn connect_lazy(url: &str) -> Result + where + ::Options: std::str::FromStr, + { PoolOptions::::new().connect_lazy(url) } @@ -353,7 +359,7 @@ impl Pool { /// Retrieves a connection and immediately begins a new transaction. pub async fn begin(&self) -> Result, Error> { - Ok(Transaction::begin(MaybePoolConnection::PoolConnection(self.acquire().await?)).await?) + Transaction::begin(MaybePoolConnection::PoolConnection(self.acquire().await?)).await } /// Attempts to retrieve a connection and immediately begins a new transaction if successful. @@ -598,7 +604,7 @@ impl FusedFuture for CloseEvent { /// get the time between the deadline and now and use that as our timeout /// /// returns `Error::PoolTimedOut` if the deadline is in the past -fn deadline_as_timeout(deadline: Instant) -> Result { +fn deadline_as_timeout(deadline: Instant) -> Result { deadline .checked_duration_since(Instant::now()) .ok_or(Error::PoolTimedOut) diff --git a/sqlx-core/src/pool/options.rs b/sqlx-core/src/pool/options.rs index 18f8106886..07f6018b55 100644 --- a/sqlx-core/src/pool/options.rs +++ b/sqlx-core/src/pool/options.rs @@ -428,7 +428,10 @@ impl PoolOptions { /// * MySQL: [`MySqlConnectOptions`][crate::mysql::MySqlConnectOptions] /// * SQLite: [`SqliteConnectOptions`][crate::sqlite::SqliteConnectOptions] /// * MSSQL: [`MssqlConnectOptions`][crate::mssql::MssqlConnectOptions] - pub async fn connect(self, url: &str) -> Result, Error> { + pub async fn connect(self, url: &str) -> Result, Error> + where + ::Options: std::str::FromStr, + { self.connect_with(url.parse()?).await } @@ -471,7 +474,10 @@ impl PoolOptions { /// * MySQL: [`MySqlConnectOptions`][crate::mysql::MySqlConnectOptions] /// * SQLite: [`SqliteConnectOptions`][crate::sqlite::SqliteConnectOptions] /// * MSSQL: [`MssqlConnectOptions`][crate::mssql::MssqlConnectOptions] - pub fn connect_lazy(self, url: &str) -> Result, Error> { + pub fn connect_lazy(self, url: &str) -> Result, Error> + where + ::Options: std::str::FromStr, + { Ok(self.connect_lazy_with(url.parse()?)) } diff --git a/sqlx-core/src/postgres/column.rs b/sqlx-core/src/postgres/column.rs index 559fd6947a..bab4da5159 100644 --- a/sqlx-core/src/postgres/column.rs +++ b/sqlx-core/src/postgres/column.rs @@ -24,7 +24,7 @@ impl Column for PgColumn { } fn name(&self) -> &str { - &*self.name + &self.name } fn type_info(&self) -> &PgTypeInfo { diff --git a/sqlx-core/src/postgres/connection/executor.rs b/sqlx-core/src/postgres/connection/executor.rs index 8785648dea..ed39bd83ad 100644 --- a/sqlx-core/src/postgres/connection/executor.rs +++ b/sqlx-core/src/postgres/connection/executor.rs @@ -48,7 +48,7 @@ async fn prepare( // next we send the PARSE command to the server conn.stream.write(Parse { - param_types: &*param_types, + param_types: ¶m_types, query: sql, statement: id, }); @@ -160,7 +160,7 @@ impl PgConnection { self.pending_ready_for_query_count += 1; } - async fn get_or_prepare<'a>( + async fn get_or_prepare( &mut self, sql: &str, parameters: &[PgTypeInfo], @@ -226,8 +226,9 @@ impl PgConnection { portal: None, statement, formats: &[PgValueFormat::Binary], - num_params: arguments.types.len() as i16, - params: &*arguments.buffer, + num_params: i16::try_from(arguments.types.len()) + .expect("too many bind parameters for i16 count"), + params: &arguments.buffer, result_formats: &[PgValueFormat::Binary], }); @@ -360,12 +361,13 @@ impl PgConnection { impl<'c> Executor<'c> for &'c mut PgConnection { type Database = Postgres; - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, mut query: E, ) -> BoxStream<'e, Result, Error>> where 'c: 'e, + 'q: 'e, E: Execute<'q, Self::Database>, { let sql = query.sql(); @@ -385,12 +387,10 @@ impl<'c> Executor<'c> for &'c mut PgConnection { }) } - fn fetch_optional<'e, 'q: 'e, E: 'q>( - self, - mut query: E, - ) -> BoxFuture<'e, Result, Error>> + fn fetch_optional<'e, 'q, E>(self, mut query: E) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, E: Execute<'q, Self::Database>, { let sql = query.sql(); @@ -412,13 +412,14 @@ impl<'c> Executor<'c> for &'c mut PgConnection { }) } - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, sql: &'q str, parameters: &'e [PgTypeInfo], ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { self.wait_until_ready().await?; @@ -432,12 +433,13 @@ impl<'c> Executor<'c> for &'c mut PgConnection { }) } - fn describe<'e, 'q: 'e>( + fn describe<'e, 'q>( self, sql: &'q str, ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { self.wait_until_ready().await?; diff --git a/sqlx-core/src/postgres/connection/sasl.rs b/sqlx-core/src/postgres/connection/sasl.rs index 5e1939c3dc..396fd141bb 100644 --- a/sqlx-core/src/postgres/connection/sasl.rs +++ b/sqlx-core/src/postgres/connection/sasl.rs @@ -126,7 +126,7 @@ pub(crate) async fn authenticate( // ClientSignature := HMAC(StoredKey, AuthMessage) let mut mac = Hmac::::new_from_slice(&stored_key).map_err(Error::protocol)?; - mac.update(&auth_message.as_bytes()); + mac.update(auth_message.as_bytes()); let client_signature = mac.finalize().into_bytes(); @@ -145,7 +145,7 @@ pub(crate) async fn authenticate( // ServerSignature := HMAC(ServerKey, AuthMessage) let mut mac = Hmac::::new_from_slice(&server_key).map_err(Error::protocol)?; - mac.update(&auth_message.as_bytes()); + mac.update(auth_message.as_bytes()); // client-final-message = client-final-message-without-proof "," proof let client_final_message = format!( @@ -183,10 +183,10 @@ fn gen_nonce() -> String { // ;; a valid "value". let nonce: String = std::iter::repeat(()) .map(|()| { - let mut c = rng.gen_range(0x21..0x7F) as u8; + let mut c: u8 = rng.gen_range(0x21u8..0x7Fu8); while c == 0x2C { - c = rng.gen_range(0x21..0x7F) as u8; + c = rng.gen_range(0x21u8..0x7Fu8); } c diff --git a/sqlx-core/src/postgres/connection/stream.rs b/sqlx-core/src/postgres/connection/stream.rs index 59b5289b8e..9f5913b06a 100644 --- a/sqlx-core/src/postgres/connection/stream.rs +++ b/sqlx-core/src/postgres/connection/stream.rs @@ -60,6 +60,19 @@ impl PgStream { self.flush().await } + #[inline] + pub(crate) fn write<'en, T>(&mut self, message: T) + where + T: Encode<'en>, + { + self.inner.write(message) + } + + #[inline] + pub(crate) fn flush(&mut self) -> impl core::future::Future> + '_ { + self.inner.flush() + } + // Expect a specific type and format pub(crate) async fn recv_expect<'de, T: Decode<'de>>( &mut self, @@ -84,7 +97,7 @@ impl PgStream { let mut header: Bytes = self.inner.read(5).await?; let format = MessageFormat::try_from_u8(header.get_u8())?; - let size = (header.get_u32() - 4) as usize; + let size = usize::try_from(header.get_u32() - 4).expect("message size should fit in usize"); let contents = self.inner.read(size).await?; @@ -203,7 +216,7 @@ fn parse_server_version(s: &str) -> Option { break; } } - _ if ch.is_digit(10) => { + _ if ch.is_ascii_digit() => { if chs.peek().is_none() { if let Ok(num) = u32::from_str(&s[from..]) { parts.push(num); diff --git a/sqlx-core/src/postgres/copy.rs b/sqlx-core/src/postgres/copy.rs index 2719f7ae54..976f4945f0 100644 --- a/sqlx-core/src/postgres/copy.rs +++ b/sqlx-core/src/postgres/copy.rs @@ -42,7 +42,7 @@ impl PgConnection { /// /// 1. by closing the connection, or: /// 2. by using another connection to kill the server process that is sending the data as shown - /// [in this StackOverflow answer](https://stackoverflow.com/a/35319598). + /// [in this StackOverflow answer](https://stackoverflow.com/a/35319598). /// /// If you don't read the stream to completion, the next time the connection is used it will /// need to read and discard all the remaining queued data, which could take some time. @@ -90,7 +90,7 @@ impl Pool { /// /// 1. by closing the connection, or: /// 2. by using another connection to kill the server process that is sending the data as shown - /// [in this StackOverflow answer](https://stackoverflow.com/a/35319598). + /// [in this StackOverflow answer](https://stackoverflow.com/a/35319598). /// /// If you don't read the stream to completion, the next time the connection is used it will /// need to read and discard all the remaining queued data, which could take some time. @@ -149,7 +149,7 @@ impl> PgCopyIn { /// Returns the number of columns expected in the input. pub fn num_columns(&self) -> usize { assert_eq!( - self.response.num_columns as usize, + usize::try_from(self.response.num_columns).expect("num_columns should be non-negative"), self.response.format_codes.len(), "num_columns does not match format_codes.len()" ); diff --git a/sqlx-core/src/postgres/io/buf_mut.rs b/sqlx-core/src/postgres/io/buf_mut.rs index d0b710293d..0fc3d86692 100644 --- a/sqlx-core/src/postgres/io/buf_mut.rs +++ b/sqlx-core/src/postgres/io/buf_mut.rs @@ -25,7 +25,7 @@ impl PgBufMutExt for Vec { f(self); // now calculate the size of what we wrote and set the length value - let size = (self.len() - offset) as i32; + let size = i32::try_from(self.len() - offset).expect("message size should fit in i32"); self[offset..(offset + 4)].copy_from_slice(&size.to_be_bytes()); } diff --git a/sqlx-core/src/postgres/listener.rs b/sqlx-core/src/postgres/listener.rs index 079de6e992..a68f412561 100644 --- a/sqlx-core/src/postgres/listener.rs +++ b/sqlx-core/src/postgres/listener.rs @@ -336,13 +336,14 @@ impl Drop for PgListener { impl<'c> Executor<'c> for &'c mut PgListener { type Database = Postgres; - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, query: E, ) -> BoxStream<'e, Result, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { futures_util::stream::once(async move { // need some basic type annotation to help the compiler a bit @@ -353,24 +354,23 @@ impl<'c> Executor<'c> for &'c mut PgListener { .boxed() } - fn fetch_optional<'e, 'q: 'e, E: 'q>( - self, - query: E, - ) -> BoxFuture<'e, Result, Error>> + fn fetch_optional<'e, 'q, E>(self, query: E) -> BoxFuture<'e, Result, Error>> where 'c: 'e, - E: Execute<'q, Self::Database>, + 'q: 'e, + E: Execute<'q, Self::Database> + 'e, { async move { self.connection().await?.fetch_optional(query).await }.boxed() } - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, query: &'q str, parameters: &'e [PgTypeInfo], ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { async move { self.connection() @@ -382,12 +382,13 @@ impl<'c> Executor<'c> for &'c mut PgListener { } #[doc(hidden)] - fn describe<'e, 'q: 'e>( + fn describe<'e, 'q>( self, query: &'q str, ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { async move { self.connection().await?.describe(query).await }.boxed() } diff --git a/sqlx-core/src/postgres/message/authentication.rs b/sqlx-core/src/postgres/message/authentication.rs index 4fb1119b79..5793c6f40e 100644 --- a/sqlx-core/src/postgres/message/authentication.rs +++ b/sqlx-core/src/postgres/message/authentication.rs @@ -162,8 +162,8 @@ impl Decode<'_> for AuthenticationSaslContinue { Ok(Self { iterations, salt, - nonce: from_utf8(&*nonce).map_err(Error::protocol)?.to_owned(), - message: from_utf8(&*buf).map_err(Error::protocol)?.to_owned(), + nonce: from_utf8(&nonce).map_err(Error::protocol)?.to_owned(), + message: from_utf8(&buf).map_err(Error::protocol)?.to_owned(), }) } } diff --git a/sqlx-core/src/postgres/message/bind.rs b/sqlx-core/src/postgres/message/bind.rs index ef250350bf..d5da29bccd 100644 --- a/sqlx-core/src/postgres/message/bind.rs +++ b/sqlx-core/src/postgres/message/bind.rs @@ -42,7 +42,11 @@ impl Encode<'_> for Bind<'_> { buf.put_statement_name(self.statement); - buf.extend(&(self.formats.len() as i16).to_be_bytes()); + buf.extend( + &i16::try_from(self.formats.len()) + .expect("formats length should fit in i16") + .to_be_bytes(), + ); for &format in self.formats { buf.extend(&(format as i16).to_be_bytes()); diff --git a/sqlx-core/src/postgres/message/data_row.rs b/sqlx-core/src/postgres/message/data_row.rs index 0f86388995..2cb1e6adf2 100644 --- a/sqlx-core/src/postgres/message/data_row.rs +++ b/sqlx-core/src/postgres/message/data_row.rs @@ -42,7 +42,7 @@ impl Decode<'_> for DataRow { if let Ok(length) = u32::try_from(length) { values.push(Some(offset..(offset + length))); - offset += length as u32; + offset += length; } else { values.push(None); } diff --git a/sqlx-core/src/postgres/message/parse.rs b/sqlx-core/src/postgres/message/parse.rs index 82fadd7e18..dd5950ddb4 100644 --- a/sqlx-core/src/postgres/message/parse.rs +++ b/sqlx-core/src/postgres/message/parse.rs @@ -1,4 +1,4 @@ -use std::i16; +// remove legacy numeric constants import use crate::io::{BufMutExt, Encode}; use crate::postgres::io::PgBufMutExt; diff --git a/sqlx-core/src/postgres/options/parse.rs b/sqlx-core/src/postgres/options/parse.rs index 30a5cf75ec..bfa1e63653 100644 --- a/sqlx-core/src/postgres/options/parse.rs +++ b/sqlx-core/src/postgres/options/parse.rs @@ -28,7 +28,7 @@ impl FromStr for PgConnectOptions { let username = url.username(); if !username.is_empty() { options = options.username( - &*percent_decode_str(username) + &percent_decode_str(username) .decode_utf8() .map_err(Error::config)?, ); @@ -36,7 +36,7 @@ impl FromStr for PgConnectOptions { if let Some(password) = url.password() { options = options.password( - &*percent_decode_str(password) + &percent_decode_str(password) .decode_utf8() .map_err(Error::config)?, ); @@ -70,29 +70,29 @@ impl FromStr for PgConnectOptions { if value.starts_with("/") { options = options.socket(&*value); } else { - options = options.host(&*value); + options = options.host(&value); } } "hostaddr" => { value.parse::().map_err(Error::config)?; - options = options.host(&*value) + options = options.host(&value) } "port" => options = options.port(value.parse().map_err(Error::config)?), - "dbname" => options = options.database(&*value), + "dbname" => options = options.database(&value), - "user" => options = options.username(&*value), + "user" => options = options.username(&value), - "password" => options = options.password(&*value), + "password" => options = options.password(&value), - "application_name" => options = options.application_name(&*value), + "application_name" => options = options.application_name(&value), "options" => { if let Some(options) = options.options.as_mut() { options.push(' '); - options.push_str(&*value); + options.push_str(&value); } else { options.options = Some(value.to_string()); } @@ -100,7 +100,7 @@ impl FromStr for PgConnectOptions { k if k.starts_with("options[") => { if let Some(key) = k.strip_prefix("options[").unwrap().strip_suffix(']') { - options = options.options([(key, &*value)]); + options = options.options([(key, &value)]); } } diff --git a/sqlx-core/src/postgres/row.rs b/sqlx-core/src/postgres/row.rs index 1b7bdf7e38..3fd9cdc00a 100644 --- a/sqlx-core/src/postgres/row.rs +++ b/sqlx-core/src/postgres/row.rs @@ -46,7 +46,7 @@ impl ColumnIndex for &'_ str { .column_names .get(*self) .ok_or_else(|| Error::ColumnNotFound((*self).into())) - .map(|v| *v) + .copied() } } diff --git a/sqlx-core/src/postgres/statement.rs b/sqlx-core/src/postgres/statement.rs index 4c01b91563..f1eecef01c 100644 --- a/sqlx-core/src/postgres/statement.rs +++ b/sqlx-core/src/postgres/statement.rs @@ -54,7 +54,7 @@ impl ColumnIndex> for &'_ str { .column_names .get(*self) .ok_or_else(|| Error::ColumnNotFound((*self).into())) - .map(|v| *v) + .copied() } } diff --git a/sqlx-core/src/postgres/type_info.rs b/sqlx-core/src/postgres/type_info.rs index c6a96b58ee..2dae7bcfa9 100644 --- a/sqlx-core/src/postgres/type_info.rs +++ b/sqlx-core/src/postgres/type_info.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use std::borrow::Cow; use std::fmt::{self, Display, Formatter}; use std::ops::Deref; @@ -540,7 +538,7 @@ impl PgType { PgType::Money => "MONEY", PgType::MoneyArray => "MONEY[]", PgType::Void => "VOID", - PgType::Custom(ty) => &*ty.name, + PgType::Custom(ty) => &ty.name, PgType::DeclareWithOid(_) => "?", PgType::DeclareWithName(name) => name, } @@ -640,7 +638,7 @@ impl PgType { PgType::Money => "money", PgType::MoneyArray => "_money", PgType::Void => "void", - PgType::Custom(ty) => &*ty.name, + PgType::Custom(ty) => &ty.name, PgType::DeclareWithOid(_) => "?", PgType::DeclareWithName(name) => name, } diff --git a/sqlx-core/src/postgres/types/array.rs b/sqlx-core/src/postgres/types/array.rs index a863d8d92c..96dc82219d 100644 --- a/sqlx-core/src/postgres/types/array.rs +++ b/sqlx-core/src/postgres/types/array.rs @@ -94,7 +94,7 @@ where T: Encode<'q, Postgres> + Type, { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull { - let type_info = if self.len() < 1 { + let type_info = if self.is_empty() { T::type_info() } else { self[0].produces().unwrap_or_else(T::type_info) diff --git a/sqlx-core/src/postgres/types/float.rs b/sqlx-core/src/postgres/types/float.rs index 1d8d684ef9..5943f258e5 100644 --- a/sqlx-core/src/postgres/types/float.rs +++ b/sqlx-core/src/postgres/types/float.rs @@ -66,7 +66,7 @@ impl Decode<'_, Postgres> for f64 { Ok(match value.format() { PgValueFormat::Binary => { if value.type_info == PgTypeInfo::NUMERIC { - return Ok(PgNumeric::decode(value.as_bytes()?)?.try_into()?); + return PgNumeric::decode(value.as_bytes()?)?.try_into(); } let buf = value.as_bytes()?; match buf.len() { diff --git a/sqlx-core/src/postgres/types/lquery.rs b/sqlx-core/src/postgres/types/lquery.rs index 93492b95f2..985a51fa80 100644 --- a/sqlx-core/src/postgres/types/lquery.rs +++ b/sqlx-core/src/postgres/types/lquery.rs @@ -75,7 +75,7 @@ impl PgLQuery { } /// creates lquery from an iterator with checking labels - pub fn from_iter(levels: I) -> Result + pub fn from_levels(levels: I) -> Result where S: Into, I: IntoIterator, @@ -104,7 +104,7 @@ impl FromStr for PgLQuery { Ok(Self { levels: s .split('.') - .map(|s| PgLQueryLevel::from_str(s)) + .map(PgLQueryLevel::from_str) .collect::>()?, }) } @@ -263,10 +263,10 @@ impl FromStr for PgLQueryVariant { fn from_str(s: &str) -> Result { let mut label_length = s.len(); - let mut rev_iter = s.bytes().rev(); + let rev_iter = s.bytes().rev(); let mut modifiers = PgLQueryVariantFlag::default(); - while let Some(b) = rev_iter.next() { + for b in rev_iter { match b { b'@' => modifiers.insert(PgLQueryVariantFlag::IN_CASE), b'*' => modifiers.insert(PgLQueryVariantFlag::ANY_END), diff --git a/sqlx-core/src/postgres/types/ltree.rs b/sqlx-core/src/postgres/types/ltree.rs index a1e7c32563..28d5dc1f15 100644 --- a/sqlx-core/src/postgres/types/ltree.rs +++ b/sqlx-core/src/postgres/types/ltree.rs @@ -103,7 +103,7 @@ impl PgLTree { } /// creates ltree from an iterator with checking labels - pub fn from_iter(labels: I) -> Result + pub fn from_labels(labels: I) -> Result where String: From, I: IntoIterator, diff --git a/sqlx-core/src/postgres/types/range.rs b/sqlx-core/src/postgres/types/range.rs index 367893119b..7770a5be08 100644 --- a/sqlx-core/src/postgres/types/range.rs +++ b/sqlx-core/src/postgres/types/range.rs @@ -447,7 +447,7 @@ where } count += 1; - if !(element.is_empty() && !quoted) { + if !element.is_empty() || quoted { let value = Some(T::decode(PgValueRef { type_info: T::type_info(), format: PgValueFormat::Text, diff --git a/sqlx-core/src/query.rs b/sqlx-core/src/query.rs index 51aea9cf3b..1a19359644 100644 --- a/sqlx-core/src/query.rs +++ b/sqlx-core/src/query.rs @@ -44,14 +44,14 @@ where #[inline] fn sql(&self) -> &'q str { match self.statement { - Either::Right(ref statement) => statement.sql(), + Either::Right(statement) => statement.sql(), Either::Left(sql) => sql, } } fn statement(&self) -> Option<&>::Statement> { match self.statement { - Either::Right(ref statement) => Some(&statement), + Either::Right(statement) => Some(statement), Either::Left(_) => None, } } @@ -293,7 +293,7 @@ where let mut f = self.mapper; Map { inner: self.inner, - mapper: move |row| f(row).and_then(|o| g(o)), + mapper: move |row| f(row).and_then(&mut g), } } diff --git a/sqlx-core/src/query_builder.rs b/sqlx-core/src/query_builder.rs index 82e2c3070f..c233b6f9df 100644 --- a/sqlx-core/src/query_builder.rs +++ b/sqlx-core/src/query_builder.rs @@ -162,7 +162,6 @@ where /// assert!(sql.ends_with("in (?, ?) ")); /// # } /// ``` - pub fn separated<'qb, Sep>(&'qb mut self, separator: Sep) -> Separated<'qb, 'args, DB, Sep> where 'args: 'qb, diff --git a/sqlx-core/src/sqlite/column.rs b/sqlx-core/src/sqlite/column.rs index f543257dd1..84d7da4239 100644 --- a/sqlx-core/src/sqlite/column.rs +++ b/sqlx-core/src/sqlite/column.rs @@ -20,7 +20,7 @@ impl Column for SqliteColumn { } fn name(&self) -> &str { - &*self.name + &self.name } fn type_info(&self) -> &SqliteTypeInfo { diff --git a/sqlx-core/src/sqlite/connection/collation.rs b/sqlx-core/src/sqlite/connection/collation.rs index ea9a50c40f..b355d6b622 100644 --- a/sqlx-core/src/sqlite/connection/collation.rs +++ b/sqlx-core/src/sqlite/connection/collation.rs @@ -128,11 +128,17 @@ where let boxed_f: *mut C = data as *mut C; debug_assert!(!boxed_f.is_null()); let s1 = { - let c_slice = slice::from_raw_parts(left_ptr as *const u8, left_len as usize); + let c_slice = slice::from_raw_parts( + left_ptr as *const u8, + usize::try_from(left_len).expect("left length should be non-negative"), + ); from_utf8_unchecked(c_slice) }; let s2 = { - let c_slice = slice::from_raw_parts(right_ptr as *const u8, right_len as usize); + let c_slice = slice::from_raw_parts( + right_ptr as *const u8, + usize::try_from(right_len).expect("right length should be non-negative"), + ); from_utf8_unchecked(c_slice) }; let t = (*boxed_f)(s1, s2); diff --git a/sqlx-core/src/sqlite/connection/establish.rs b/sqlx-core/src/sqlite/connection/establish.rs index 9cdf110b78..d6e8514145 100644 --- a/sqlx-core/src/sqlite/connection/establish.rs +++ b/sqlx-core/src/sqlite/connection/establish.rs @@ -28,7 +28,7 @@ enum SqliteLoadExtensionMode { } impl SqliteLoadExtensionMode { - fn as_int(self) -> c_int { + fn into_int(self) -> c_int { match self { SqliteLoadExtensionMode::Enable => 1, SqliteLoadExtensionMode::DisableAll => 0, @@ -160,7 +160,7 @@ impl EstablishParams { let status = sqlite3_db_config( db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, - mode.as_int(), + mode.into_int(), null::(), ); diff --git a/sqlx-core/src/sqlite/connection/execute.rs b/sqlx-core/src/sqlite/connection/execute.rs index b753fa3f0a..a5adedce94 100644 --- a/sqlx-core/src/sqlite/connection/execute.rs +++ b/sqlx-core/src/sqlite/connection/execute.rs @@ -71,7 +71,7 @@ impl Iterator for ExecuteIter<'_> { let mut statement = match self.statement.prepare_next(self.handle) { Ok(Some(statement)) => statement, Ok(None) => return None, - Err(e) => return Some(Err(e.into())), + Err(e) => return Some(Err(e)), }; self.goto_next = false; @@ -99,8 +99,8 @@ impl Iterator for ExecuteIter<'_> { Some(Ok(Either::Right(SqliteRow::current( &statement.handle, - &statement.columns, - &statement.column_names, + statement.columns, + statement.column_names, )))) } Ok(false) => { diff --git a/sqlx-core/src/sqlite/connection/executor.rs b/sqlx-core/src/sqlite/connection/executor.rs index 69ed29b92d..f0cd9f53d8 100644 --- a/sqlx-core/src/sqlite/connection/executor.rs +++ b/sqlx-core/src/sqlite/connection/executor.rs @@ -12,12 +12,13 @@ use futures_util::{TryFutureExt, TryStreamExt}; impl<'c> Executor<'c> for &'c mut SqliteConnection { type Database = Sqlite; - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, mut query: E, ) -> BoxStream<'e, Result, Error>> where 'c: 'e, + 'q: 'e, E: Execute<'q, Self::Database>, { let sql = query.sql(); @@ -32,12 +33,13 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection { ) } - fn fetch_optional<'e, 'q: 'e, E: 'q>( + fn fetch_optional<'e, 'q, E>( self, mut query: E, ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, E: Execute<'q, Self::Database>, { let sql = query.sql(); @@ -63,13 +65,14 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection { }) } - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, sql: &'q str, _parameters: &[SqliteTypeInfo], ) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(async move { let statement = self.worker.prepare(sql).await?; @@ -82,9 +85,10 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection { } #[doc(hidden)] - fn describe<'e, 'q: 'e>(self, sql: &'q str) -> BoxFuture<'e, Result, Error>> + fn describe<'e, 'q>(self, sql: &'q str) -> BoxFuture<'e, Result, Error>> where 'c: 'e, + 'q: 'e, { Box::pin(self.worker.describe(sql)) } diff --git a/sqlx-core/src/sqlite/connection/explain.rs b/sqlx-core/src/sqlite/connection/explain.rs index ace42616f8..e05b89d573 100644 --- a/sqlx-core/src/sqlite/connection/explain.rs +++ b/sqlx-core/src/sqlite/connection/explain.rs @@ -194,7 +194,7 @@ impl CursorDataType { ) } - fn from_dense_record(record: &Vec) -> Self { + fn from_dense_record(record: &[ColumnType]) -> Self { Self::Normal((0..).zip(record.iter().copied()).collect()) } @@ -203,7 +203,8 @@ impl CursorDataType { Self::Normal(record) => { let mut rowdata = vec![ColumnType::default(); record.len()]; for (idx, col) in record.iter() { - rowdata[*idx as usize] = col.clone(); + let pos = usize::try_from(*idx).expect("column index should be non-negative"); + rowdata[pos] = *col; } rowdata } @@ -228,7 +229,6 @@ impl CursorDataType { } } -#[allow(clippy::wildcard_in_or_patterns)] fn affinity_to_type(affinity: u8) -> DataType { match affinity { SQLITE_AFF_BLOB => DataType::Blob, @@ -237,11 +237,10 @@ fn affinity_to_type(affinity: u8) -> DataType { SQLITE_AFF_REAL => DataType::Float, SQLITE_AFF_TEXT => DataType::Text, - SQLITE_AFF_NONE | _ => DataType::Null, + _ => DataType::Null, } } -#[allow(clippy::wildcard_in_or_patterns)] fn opcode_to_type(op: &str) -> DataType { match op { OP_REAL => DataType::Float, @@ -249,7 +248,7 @@ fn opcode_to_type(op: &str) -> DataType { OP_AND | OP_OR => DataType::Bool, OP_ROWID | OP_COUNT | OP_INT64 | OP_INTEGER => DataType::Int64, OP_STRING8 => DataType::Text, - OP_COLUMN | _ => DataType::Null, + _ => DataType::Null, } } @@ -306,7 +305,7 @@ fn root_block_columns( ); } - return Ok(row_info); + Ok(row_info) } #[derive(Debug, Clone, PartialEq)] @@ -364,14 +363,16 @@ pub(super) fn explain( OP_INIT => { // start at state.visited[state.program_i] = true; - state.program_i = p2 as usize; + state.program_i = + usize::try_from(p2).expect("opcode p2 should be non-negative"); continue; } OP_GOTO => { // goto state.visited[state.program_i] = true; - state.program_i = p2 as usize; + state.program_i = + usize::try_from(p2).expect("opcode p2 should be non-negative"); continue; } @@ -388,7 +389,8 @@ pub(super) fn explain( state.visited[state.program_i] = true; let mut branch_state = state.clone(); - branch_state.program_i = p2 as usize; + branch_state.program_i = + usize::try_from(p2).expect("branch p2 should be non-negative"); states.push(branch_state); state.program_i += 1; @@ -401,7 +403,8 @@ pub(super) fn explain( state.r.insert(p1, RegDataType::Int(p3)); if p2 != 0 { - state.program_i = p2 as usize; + state.program_i = + usize::try_from(p2).expect("opcode p2 should be non-negative"); } else { state.program_i += 1; } @@ -412,11 +415,12 @@ pub(super) fn explain( // jump to p2 of the yield instruction pointed at by register p1 state.visited[state.program_i] = true; if let Some(RegDataType::Int(yield_i)) = state.r.get(&p1) { - if let Some((_, yield_op, _, yield_p2, _, _)) = - program.get(*yield_i as usize) - { + if let Some((_, yield_op, _, yield_p2, _, _)) = program.get( + usize::try_from(*yield_i).expect("yield index should be non-negative"), + ) { if OP_YIELD == yield_op.as_str() { - state.program_i = (*yield_p2) as usize; + state.program_i = usize::try_from(*yield_p2) + .expect("yield p2 should be non-negative"); state.r.remove(&p1); continue; } else { @@ -434,7 +438,8 @@ pub(super) fn explain( // jump to the instruction after the instruction pointed at by register p1 state.visited[state.program_i] = true; if let Some(RegDataType::Int(return_i)) = state.r.get(&p1) { - state.program_i = (*return_i + 1) as usize; + state.program_i = usize::try_from(*return_i + 1) + .expect("return index should be non-negative"); state.r.remove(&p1); continue; } else { @@ -450,16 +455,23 @@ pub(super) fn explain( //if yielding to a yield operation, go to the NEXT instruction after that instruction if program - .get(*yield_i as usize) + .get( + usize::try_from(*yield_i) + .expect("yield index should be non-negative"), + ) .map(|(_, yield_op, _, _, _, _)| yield_op.as_str()) == Some(OP_YIELD) { - state.program_i = (*yield_i + 1) as usize; - *yield_i = program_i as i64; + state.program_i = usize::try_from(*yield_i + 1) + .expect("yield+1 should be non-negative"); + *yield_i = + i64::try_from(program_i).expect("program index should fit in i64"); continue; } else { - state.program_i = *yield_i as usize; - *yield_i = program_i as i64; + state.program_i = usize::try_from(*yield_i) + .expect("yield index should be non-negative"); + *yield_i = + i64::try_from(program_i).expect("program index should fit in i64"); continue; } } else { @@ -472,15 +484,18 @@ pub(super) fn explain( state.visited[state.program_i] = true; let mut branch_state = state.clone(); - branch_state.program_i = p1 as usize; + branch_state.program_i = + usize::try_from(p1).expect("branch p1 should be non-negative"); states.push(branch_state); let mut branch_state = state.clone(); - branch_state.program_i = p2 as usize; + branch_state.program_i = + usize::try_from(p2).expect("branch p2 should be non-negative"); states.push(branch_state); let mut branch_state = state.clone(); - branch_state.program_i = p3 as usize; + branch_state.program_i = + usize::try_from(p3).expect("branch p3 should be non-negative"); states.push(branch_state); } @@ -515,7 +530,9 @@ pub(super) fn explain( OP_MAKE_RECORD => { // p3 = Record([p1 .. p1 + p2]) - let mut record = Vec::with_capacity(p2 as usize); + let mut record = Vec::with_capacity( + usize::try_from(p2).expect("record len should be non-negative"), + ); for reg in p1..p1 + p2 { record.push( state @@ -565,7 +582,12 @@ pub(super) fn explain( //Create a new pointer which is referenced by p1 state.p.insert( p1, - CursorDataType::from_dense_record(&vec![ColumnType::null(); p2 as usize]), + CursorDataType::from_dense_record(&vec![ + ColumnType::null(); + usize::try_from(p2).expect( + "record len should be non-negative" + ) + ]), ); } @@ -666,7 +688,7 @@ pub(super) fn explain( state.r.insert( p2, RegDataType::Single(ColumnType { - datatype: opcode_to_type(&opcode), + datatype: opcode_to_type(opcode), nullable: Some(false), }), ); @@ -784,8 +806,7 @@ pub(super) fn explain( while let Some(state) = result_states.pop() { // find the datatype info from each ResultRow execution if let Some(result) = state.result { - let mut idx = 0; - for (this_type, this_nullable) in result { + for (idx, (this_type, this_nullable)) in result.into_iter().enumerate() { if output.len() == idx { output.push(this_type); } else if output[idx].is_none() @@ -804,7 +825,6 @@ pub(super) fn explain( } else { nullable[idx] = this_nullable; } - idx += 1; } } } diff --git a/sqlx-core/src/sqlite/connection/function.rs b/sqlx-core/src/sqlite/connection/function.rs index cc49adbe4b..ce90511f0b 100644 --- a/sqlx-core/src/sqlite/connection/function.rs +++ b/sqlx-core/src/sqlite/connection/function.rs @@ -92,7 +92,7 @@ impl SqliteFunctionCtx { sqlite3_result_text( self.ctx, text.as_ptr() as *const c_char, - text.len() as c_int, + c_int::try_from(text.len()).expect("text length should fit in c_int"), SQLITE_TRANSIENT(), ); } @@ -100,7 +100,7 @@ impl SqliteFunctionCtx { sqlite3_result_blob( self.ctx, blob.as_ptr() as *const c_void, - blob.len() as c_int, + c_int::try_from(blob.len()).expect("blob length should fit in c_int"), SQLITE_TRANSIENT(), ); } @@ -124,7 +124,8 @@ impl SqliteFunctionCtx { sqlite3_result_error( self.ctx, error_str.as_ptr(), - error_str.as_bytes().len() as c_int, + c_int::try_from(error_str.as_bytes().len()) + .expect("error string length should fit in c_int"), ); } } diff --git a/sqlx-core/src/sqlite/connection/worker.rs b/sqlx-core/src/sqlite/connection/worker.rs index 9bf2d825d1..218d101fc6 100644 --- a/sqlx-core/src/sqlite/connection/worker.rs +++ b/sqlx-core/src/sqlite/connection/worker.rs @@ -124,12 +124,11 @@ impl ConnectionWorker { for cmd in command_rx { match cmd { Command::Prepare { query, tx } => { - tx.send(prepare(&mut conn, &query).map(|prepared| { + tx.send(prepare(&mut conn, &query).inspect(|_| { update_cached_statements_size( &conn, &shared.cached_statements_size, ); - prepared })) .ok(); } diff --git a/sqlx-core/src/sqlite/mod.rs b/sqlx-core/src/sqlite/mod.rs index e6349d9532..b7fca0edcd 100644 --- a/sqlx-core/src/sqlite/mod.rs +++ b/sqlx-core/src/sqlite/mod.rs @@ -1,9 +1,9 @@ //! **SQLite** database driver. +#![allow(unsafe_code)] // SQLite is a C library. All interactions require FFI which is unsafe. // All unsafe blocks should have comments pointing to SQLite docs and ensuring that we maintain // invariants. -#![allow(unsafe_code)] pub use arguments::{SqliteArgumentValue, SqliteArguments}; pub use column::SqliteColumn; diff --git a/sqlx-core/src/sqlite/row.rs b/sqlx-core/src/sqlite/row.rs index 6caefd52b2..927cec9400 100644 --- a/sqlx-core/src/sqlite/row.rs +++ b/sqlx-core/src/sqlite/row.rs @@ -1,5 +1,3 @@ -#![allow(clippy::rc_buffer)] - use std::sync::Arc; use crate::HashMap; @@ -14,7 +12,7 @@ use crate::sqlite::{Sqlite, SqliteColumn, SqliteValue, SqliteValueRef}; /// Implementation of [`Row`] for SQLite. pub struct SqliteRow { pub(crate) values: Box<[SqliteValue]>, - pub(crate) columns: Arc>, + pub(crate) columns: Arc<[SqliteColumn]>, pub(crate) column_names: Arc>, } @@ -32,7 +30,7 @@ unsafe impl Sync for SqliteRow {} impl SqliteRow { pub(crate) fn current( statement: &StatementHandle, - columns: &Arc>, + columns: &Arc<[SqliteColumn]>, column_names: &Arc>, ) -> Self { let size = statement.column_count(); @@ -75,7 +73,7 @@ impl ColumnIndex for &'_ str { row.column_names .get(*self) .ok_or_else(|| Error::ColumnNotFound((*self).into())) - .map(|v| *v) + .copied() } } diff --git a/sqlx-core/src/sqlite/statement/handle.rs b/sqlx-core/src/sqlite/statement/handle.rs index e3dd9e4787..1c556f53a2 100644 --- a/sqlx-core/src/sqlite/statement/handle.rs +++ b/sqlx-core/src/sqlite/statement/handle.rs @@ -71,7 +71,8 @@ impl StatementHandle { #[inline] pub(crate) fn column_count(&self) -> usize { // https://sqlite.org/c3ref/column_count.html - unsafe { sqlite3_column_count(self.0.as_ptr()) as usize } + let count: i32 = unsafe { sqlite3_column_count(self.0.as_ptr()) }; + usize::try_from(count).expect("sqlite3_column_count returned a negative value") } #[inline] @@ -79,14 +80,18 @@ impl StatementHandle { // returns the number of changes of the *last* statement; not // necessarily this statement. // https://sqlite.org/c3ref/changes.html - unsafe { sqlite3_changes(self.db_handle()) as u64 } + let changes: i32 = unsafe { sqlite3_changes(self.db_handle()) }; + u64::try_from(changes).expect("sqlite3_changes returned a negative value") } #[inline] pub(crate) fn column_name(&self, index: usize) -> &str { // https://sqlite.org/c3ref/column_name.html unsafe { - let name = sqlite3_column_name(self.0.as_ptr(), index as c_int); + let name = sqlite3_column_name( + self.0.as_ptr(), + c_int::try_from(index).expect("column_name index exceeds c_int"), + ); debug_assert!(!name.is_null()); from_utf8_unchecked(CStr::from_ptr(name).to_bytes()) @@ -107,7 +112,10 @@ impl StatementHandle { #[inline] pub(crate) fn column_decltype(&self, index: usize) -> Option { unsafe { - let decl = sqlite3_column_decltype(self.0.as_ptr(), index as c_int); + let decl = sqlite3_column_decltype( + self.0.as_ptr(), + c_int::try_from(index).expect("column_decltype index exceeds c_int"), + ); if decl.is_null() { // If the Nth column of the result set is an expression or subquery, // then a NULL pointer is returned. @@ -130,9 +138,10 @@ impl StatementHandle { // sqlite3_finalize() or until the statement is automatically reprepared by the // first call to sqlite3_step() for a particular run or until the same information // is requested again in a different encoding. - let db_name = sqlite3_column_database_name(self.0.as_ptr(), index as c_int); - let table_name = sqlite3_column_table_name(self.0.as_ptr(), index as c_int); - let origin_name = sqlite3_column_origin_name(self.0.as_ptr(), index as c_int); + let idx_cint = c_int::try_from(index).expect("index exceeds c_int"); + let db_name = sqlite3_column_database_name(self.0.as_ptr(), idx_cint); + let table_name = sqlite3_column_table_name(self.0.as_ptr(), idx_cint); + let origin_name = sqlite3_column_origin_name(self.0.as_ptr(), idx_cint); if db_name.is_null() || table_name.is_null() || origin_name.is_null() { return Ok(None); @@ -174,7 +183,8 @@ impl StatementHandle { #[inline] pub(crate) fn bind_parameter_count(&self) -> usize { // https://www.sqlite.org/c3ref/bind_parameter_count.html - unsafe { sqlite3_bind_parameter_count(self.0.as_ptr()) as usize } + let count: i32 = unsafe { sqlite3_bind_parameter_count(self.0.as_ptr()) }; + usize::try_from(count).expect("sqlite3_bind_parameter_count returned negative") } // Name Of A Host Parameter @@ -183,7 +193,10 @@ impl StatementHandle { pub(crate) fn bind_parameter_name(&self, index: usize) -> Option<&str> { unsafe { // https://www.sqlite.org/c3ref/bind_parameter_name.html - let name = sqlite3_bind_parameter_name(self.0.as_ptr(), index as c_int); + let name = sqlite3_bind_parameter_name( + self.0.as_ptr(), + c_int::try_from(index).expect("bind_parameter_name index exceeds c_int"), + ); if name.is_null() { return None; } @@ -200,7 +213,7 @@ impl StatementHandle { unsafe { sqlite3_bind_blob64( self.0.as_ptr(), - index as c_int, + c_int::try_from(index).expect("bind index exceeds c_int"), v.as_ptr() as *const c_void, v.len() as u64, SQLITE_TRANSIENT(), @@ -213,33 +226,56 @@ impl StatementHandle { unsafe { sqlite3_bind_text64( self.0.as_ptr(), - index as c_int, + c_int::try_from(index).expect("bind index exceeds c_int"), v.as_ptr() as *const c_char, v.len() as u64, SQLITE_TRANSIENT(), - SQLITE_UTF8 as u8, + u8::try_from(SQLITE_UTF8).expect("SQLITE_UTF8 fits in u8"), ) } } #[inline] pub(crate) fn bind_int(&self, index: usize, v: i32) -> c_int { - unsafe { sqlite3_bind_int(self.0.as_ptr(), index as c_int, v as c_int) } + unsafe { + sqlite3_bind_int( + self.0.as_ptr(), + c_int::try_from(index).expect("bind index exceeds c_int"), + v as c_int, + ) + } } #[inline] pub(crate) fn bind_int64(&self, index: usize, v: i64) -> c_int { - unsafe { sqlite3_bind_int64(self.0.as_ptr(), index as c_int, v) } + unsafe { + sqlite3_bind_int64( + self.0.as_ptr(), + c_int::try_from(index).expect("bind index exceeds c_int"), + v, + ) + } } #[inline] pub(crate) fn bind_double(&self, index: usize, v: f64) -> c_int { - unsafe { sqlite3_bind_double(self.0.as_ptr(), index as c_int, v) } + unsafe { + sqlite3_bind_double( + self.0.as_ptr(), + c_int::try_from(index).expect("bind index exceeds c_int"), + v, + ) + } } #[inline] pub(crate) fn bind_null(&self, index: usize) -> c_int { - unsafe { sqlite3_bind_null(self.0.as_ptr(), index as c_int) } + unsafe { + sqlite3_bind_null( + self.0.as_ptr(), + c_int::try_from(index).expect("bind_null index exceeds c_int"), + ) + } } // result values from the query @@ -247,32 +283,38 @@ impl StatementHandle { #[inline] pub(crate) fn column_type(&self, index: usize) -> c_int { - unsafe { sqlite3_column_type(self.0.as_ptr(), index as c_int) } + unsafe { sqlite3_column_type(self.0.as_ptr(), index.try_into().unwrap_or(c_int::MAX)) } } #[inline] pub(crate) fn column_int(&self, index: usize) -> i32 { - unsafe { sqlite3_column_int(self.0.as_ptr(), index as c_int) as i32 } + unsafe { + sqlite3_column_int(self.0.as_ptr(), index.try_into().unwrap_or(c_int::MAX)) as i32 + } } #[inline] pub(crate) fn column_int64(&self, index: usize) -> i64 { - unsafe { sqlite3_column_int64(self.0.as_ptr(), index as c_int) as i64 } + unsafe { + sqlite3_column_int64(self.0.as_ptr(), index.try_into().unwrap_or(c_int::MAX)) as i64 + } } #[inline] pub(crate) fn column_double(&self, index: usize) -> f64 { - unsafe { sqlite3_column_double(self.0.as_ptr(), index as c_int) } + unsafe { sqlite3_column_double(self.0.as_ptr(), index.try_into().unwrap_or(c_int::MAX)) } } #[inline] pub(crate) fn column_value(&self, index: usize) -> *mut sqlite3_value { - unsafe { sqlite3_column_value(self.0.as_ptr(), index as c_int) } + unsafe { sqlite3_column_value(self.0.as_ptr(), index.try_into().unwrap_or(c_int::MAX)) } } pub(crate) fn column_blob(&self, index: usize) -> &[u8] { - let index = index as c_int; - let len = unsafe { sqlite3_column_bytes(self.0.as_ptr(), index) } as usize; + let index: c_int = index.try_into().unwrap_or(c_int::MAX); + let len: usize = unsafe { sqlite3_column_bytes(self.0.as_ptr(), index) } + .try_into() + .unwrap_or(0); if len == 0 { // empty blobs are NULL so just return an empty slice diff --git a/sqlx-core/src/sqlite/statement/mod.rs b/sqlx-core/src/sqlite/statement/mod.rs index 5e68eb7b03..db4d454133 100644 --- a/sqlx-core/src/sqlite/statement/mod.rs +++ b/sqlx-core/src/sqlite/statement/mod.rs @@ -16,11 +16,10 @@ pub(crate) use handle::StatementHandle; pub(crate) use r#virtual::VirtualStatement; #[derive(Debug, Clone)] -#[allow(clippy::rc_buffer)] pub struct SqliteStatement<'q> { pub(crate) sql: Cow<'q, str>, pub(crate) parameters: usize, - pub(crate) columns: Arc>, + pub(crate) columns: Arc<[SqliteColumn]>, pub(crate) column_names: Arc>, } diff --git a/sqlx-core/src/sqlite/statement/unlock_notify.rs b/sqlx-core/src/sqlite/statement/unlock_notify.rs index c11af02885..ebb8abf9d0 100644 --- a/sqlx-core/src/sqlite/statement/unlock_notify.rs +++ b/sqlx-core/src/sqlite/statement/unlock_notify.rs @@ -27,7 +27,10 @@ pub unsafe fn wait(conn: *mut sqlite3) -> Result<(), SqliteError> { unsafe extern "C" fn unlock_notify_cb(ptr: *mut *mut c_void, len: c_int) { let ptr = ptr as *mut &Notify; - let slice = slice::from_raw_parts(ptr, len as usize); + let slice = slice::from_raw_parts( + ptr, + usize::try_from(len).expect("unlock_notify callback length negative"), + ); for notify in slice { notify.fire(); diff --git a/sqlx-core/src/sqlite/statement/virtual.rs b/sqlx-core/src/sqlite/statement/virtual.rs index 18a0e8804d..c3e9941420 100644 --- a/sqlx-core/src/sqlite/statement/virtual.rs +++ b/sqlx-core/src/sqlite/statement/virtual.rs @@ -1,5 +1,3 @@ -#![allow(clippy::rc_buffer)] - use crate::error::Error; use crate::ext::ustr::UStr; use crate::sqlite::connection::ConnectionHandle; @@ -11,10 +9,10 @@ use libsqlite3_sys::{ sqlite3, sqlite3_prepare_v3, sqlite3_stmt, SQLITE_OK, SQLITE_PREPARE_PERSISTENT, }; use smallvec::SmallVec; +use std::cmp; use std::os::raw::c_char; use std::ptr::{null, null_mut, NonNull}; use std::sync::Arc; -use std::{cmp, i32}; // A virtual statement consists of *zero* or more raw SQLite3 statements. We chop up a SQL statement // on `;` to support multiple statements in one query. @@ -41,7 +39,7 @@ pub struct VirtualStatement { pub(crate) handles: SmallVec<[StatementHandle; 1]>, // each set of columns - pub(crate) columns: SmallVec<[Arc>; 1]>, + pub(crate) columns: SmallVec<[Arc<[SqliteColumn]>; 1]>, // each set of column names pub(crate) column_names: SmallVec<[Arc>; 1]>, @@ -49,7 +47,7 @@ pub struct VirtualStatement { pub struct PreparedStatement<'a> { pub(crate) handle: &'a mut StatementHandle, - pub(crate) columns: &'a Arc>, + pub(crate) columns: &'a Arc<[SqliteColumn]>, pub(crate) column_names: &'a Arc>, } @@ -72,7 +70,7 @@ impl VirtualStatement { } } - if query.len() > i32::max_value() as usize { + if query.len() > i32::MAX as usize { return Err(err_protocol!( "query string must be smaller than {} bytes", i32::MAX @@ -132,7 +130,7 @@ impl VirtualStatement { } self.handles.push(statement); - self.columns.push(Arc::new(columns)); + self.columns.push(Arc::<[SqliteColumn]>::from(columns)); self.column_names.push(Arc::new(column_names)); } } @@ -183,7 +181,7 @@ fn prepare( let mut tail: *const c_char = null(); let query_ptr = query.as_ptr() as *const c_char; - let query_len = query.len() as i32; + let query_len: i32 = query.len().try_into().unwrap_or(i32::MAX); // let status = unsafe { @@ -191,7 +189,7 @@ fn prepare( conn, query_ptr, query_len, - flags as u32, + flags, &mut statement_handle, &mut tail, ) @@ -200,7 +198,8 @@ fn prepare( if status != SQLITE_OK { // SAFETY: query is derived from original_query by successive calls to `advance()` let index_in_original_query = - unsafe { query.as_ptr().offset_from(original_query.as_ptr()) } as usize; + usize::try_from(unsafe { query.as_ptr().offset_from(original_query.as_ptr()) }) + .expect("query pointer offset should be non-negative"); return Err(SqliteError::new(conn) .with_statement_start_index(index_in_original_query) .into()); @@ -210,7 +209,7 @@ fn prepare( // statement in zSql. these routines only compile the first statement, // so tail is left pointing to what remains un-compiled. - let n = (tail as usize) - (query_ptr as usize); + let n = (tail as usize).saturating_sub(query_ptr as usize); query.advance(n); if let Some(handle) = NonNull::new(statement_handle) { diff --git a/sqlx-core/src/sqlite/types/float.rs b/sqlx-core/src/sqlite/types/float.rs index d8b2c3bd17..cce91b2fd8 100644 --- a/sqlx-core/src/sqlite/types/float.rs +++ b/sqlx-core/src/sqlite/types/float.rs @@ -21,7 +21,13 @@ impl<'q> Encode<'q, Sqlite> for f32 { impl<'r> Decode<'r, Sqlite> for f32 { fn decode(value: SqliteValueRef<'r>) -> Result { - Ok(value.double() as f32) + let dbl = value.double(); + let clamped = if dbl.is_finite() { + dbl.clamp(f32::MIN as f64, f32::MAX as f64) + } else { + dbl + }; + Ok(clamped as f32) } } diff --git a/sqlx-core/src/sqlite/value.rs b/sqlx-core/src/sqlite/value.rs index e193b042e0..fdd575c9e4 100644 --- a/sqlx-core/src/sqlite/value.rs +++ b/sqlx-core/src/sqlite/value.rs @@ -126,7 +126,11 @@ impl SqliteValue { } fn blob(&self) -> &[u8] { - let len = unsafe { sqlite3_value_bytes(self.handle.0.as_ptr()) } as usize; + let len: usize = + match usize::try_from(unsafe { sqlite3_value_bytes(self.handle.0.as_ptr()) }) { + Ok(v) => v, + Err(_) => return &[], + }; if len == 0 { // empty blobs are NULL so just return an empty slice diff --git a/sqlx-core/src/transaction.rs b/sqlx-core/src/transaction.rs index 76d5ac85ba..64db6ada31 100644 --- a/sqlx-core/src/transaction.rs +++ b/sqlx-core/src/transaction.rs @@ -103,7 +103,7 @@ macro_rules! impl_executor_for_transaction { { type Database = $DB; - fn fetch_many<'e, 'q: 'e, E: 'q>( + fn fetch_many<'e, 'q, E>( self, query: E, ) -> futures_core::stream::BoxStream< @@ -115,23 +115,25 @@ macro_rules! impl_executor_for_transaction { > where 't: 'e, - E: crate::executor::Execute<'q, Self::Database>, + 'q: 'e, + E: crate::executor::Execute<'q, Self::Database> + 'e, { (&mut **self).fetch_many(query) } - fn fetch_optional<'e, 'q: 'e, E: 'q>( + fn fetch_optional<'e, 'q, E>( self, query: E, ) -> futures_core::future::BoxFuture<'e, Result, crate::error::Error>> where 't: 'e, - E: crate::executor::Execute<'q, Self::Database>, + 'q: 'e, + E: crate::executor::Execute<'q, Self::Database> + 'e, { (&mut **self).fetch_optional(query) } - fn prepare_with<'e, 'q: 'e>( + fn prepare_with<'e, 'q>( self, sql: &'q str, parameters: &'e [::TypeInfo], @@ -144,12 +146,13 @@ macro_rules! impl_executor_for_transaction { > where 't: 'e, + 'q: 'e, { (&mut **self).prepare_with(sql, parameters) } #[doc(hidden)] - fn describe<'e, 'q: 'e>( + fn describe<'e, 'q>( self, query: &'q str, ) -> futures_core::future::BoxFuture< @@ -158,6 +161,7 @@ macro_rules! impl_executor_for_transaction { > where 't: 'e, + 'q: 'e, { (&mut **self).describe(query) } diff --git a/sqlx-macros/src/database/mod.rs b/sqlx-macros/src/database/mod.rs index cbc0c5dd61..05cb7a7bff 100644 --- a/sqlx-macros/src/database/mod.rs +++ b/sqlx-macros/src/database/mod.rs @@ -10,8 +10,10 @@ pub enum ParamChecking { pub trait DatabaseExt: Database { const DATABASE_PATH: &'static str; const ROW_PATH: &'static str; + #[allow(dead_code)] const NAME: &'static str; + #[allow(dead_code)] const PARAM_CHECKING: ParamChecking; fn db_path() -> syn::Path { diff --git a/sqlx-macros/src/lib.rs b/sqlx-macros/src/lib.rs index 5e12554292..61e4247249 100644 --- a/sqlx-macros/src/lib.rs +++ b/sqlx-macros/src/lib.rs @@ -1,4 +1,3 @@ -#![allow(clippy::large_enum_variant)] #![cfg_attr( not(any( feature = "postgres", diff --git a/sqlx-macros/src/query/input.rs b/sqlx-macros/src/query/input.rs index f3bce4a333..cc9b43b9e5 100644 --- a/sqlx-macros/src/query/input.rs +++ b/sqlx-macros/src/query/input.rs @@ -28,7 +28,7 @@ enum QuerySrc { } pub enum RecordType { - Given(Type), + Given(Box), Scalar, Generated, } @@ -69,7 +69,7 @@ impl Parse for QueryMacroInput { return Err(input.error("colliding `scalar` or `record` key")); } - record_type = RecordType::Given(input.parse()?); + record_type = RecordType::Given(Box::new(input.parse()?)); } else if key == "scalar" { if !matches!(record_type, RecordType::Generated) { return Err(input.error("colliding `scalar` or `record` key")); diff --git a/sqlx-macros/src/query/mod.rs b/sqlx-macros/src/query/mod.rs index 7ba3bd2cc4..c69a227a52 100644 --- a/sqlx-macros/src/query/mod.rs +++ b/sqlx-macros/src/query/mod.rs @@ -154,7 +154,7 @@ pub fn expand_input(input: QueryMacroInput) -> crate::Result { offline: false, database_url: Some(db_url), .. - } => expand_from_db(input, &db_url), + } => expand_from_db(input, db_url), #[cfg(feature = "offline")] _ => { @@ -246,9 +246,7 @@ fn expand_from_db(input: QueryMacroInput, db_url: &str) -> crate::Result { - return Err(format!("Missing expansion needed for: {:?}", item).into()); - } + item => Err(format!("Missing expansion needed for: {:?}", item).into()), } }) } diff --git a/sqlx-macros/src/query/output.rs b/sqlx-macros/src/query/output.rs index a08451ed87..c33782ccd8 100644 --- a/sqlx-macros/src/query/output.rs +++ b/sqlx-macros/src/query/output.rs @@ -64,7 +64,7 @@ enum ColumnNullabilityOverride { } enum ColumnTypeOverride { - Exact(Type), + Exact(Box), Wildcard, None, } @@ -300,7 +300,7 @@ impl Parse for ColumnOverride { if let Type::Infer(_) = ty { ColumnTypeOverride::Wildcard } else { - ColumnTypeOverride::Exact(ty) + ColumnTypeOverride::Exact(Box::new(ty)) } } else { ColumnTypeOverride::None diff --git a/sqlx-macros/src/test_attr.rs b/sqlx-macros/src/test_attr.rs index c006ff7294..4a743d1581 100644 --- a/sqlx-macros/src/test_attr.rs +++ b/sqlx-macros/src/test_attr.rs @@ -1,13 +1,15 @@ -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use quote::quote; use syn::punctuated::Punctuated; use syn::{LitStr, Token}; +#[allow(dead_code)] struct Args { fixtures: Vec, migrations: MigrationsOpt, } +#[allow(dead_code)] enum MigrationsOpt { InferredPath, ExplicitPath(LitStr), @@ -112,7 +114,7 @@ fn expand_advanced( quote! { args.migrator(&#migrator); } } MigrationsOpt::InferredPath if !inputs.is_empty() => { - let migrations_path = crate::common::resolve_path("./migrations", Span::call_site())?; + let migrations_path = crate::common::resolve_path("./migrations", input.sig.span())?; if migrations_path.is_dir() { let migrator = crate::migrate::expand_migrator(&migrations_path)?; diff --git a/sqlx-rt/src/rt_async_std.rs b/sqlx-rt/src/rt_async_std.rs index aeca8541ab..7fe094de65 100644 --- a/sqlx-rt/src/rt_async_std.rs +++ b/sqlx-rt/src/rt_async_std.rs @@ -1,7 +1,7 @@ pub use async_std::{ - self, fs, future::timeout, io::prelude::ReadExt as AsyncReadExt, - io::prelude::WriteExt as AsyncWriteExt, io::Read as AsyncRead, io::Write as AsyncWrite, - net::TcpStream, sync::Mutex as AsyncMutex, task::sleep, task::spawn, task::yield_now, + self, io::prelude::ReadExt as AsyncReadExt, io::prelude::WriteExt as AsyncWriteExt, + io::Read as AsyncRead, io::Write as AsyncWrite, net::TcpStream, sync::Mutex as AsyncMutex, + task::sleep, task::spawn, task::yield_now, }; #[cfg(unix)] diff --git a/sqlx-test/src/lib.rs b/sqlx-test/src/lib.rs index 6895dea46a..b4de3861f8 100644 --- a/sqlx-test/src/lib.rs +++ b/sqlx-test/src/lib.rs @@ -12,6 +12,7 @@ pub fn setup_if_needed() { pub async fn new() -> anyhow::Result where DB: Database, + ::Options: std::str::FromStr, { setup_if_needed(); @@ -23,6 +24,7 @@ where pub async fn pool() -> anyhow::Result> where DB: Database, + ::Options: std::str::FromStr, { setup_if_needed(); diff --git a/src/macros/mod.rs b/src/macros/mod.rs index 5f33d70a74..0b79fcc28a 100644 --- a/src/macros/mod.rs +++ b/src/macros/mod.rs @@ -42,8 +42,8 @@ /// † Only callable if the query returns no columns; otherwise it's assumed the query *may* return at least one row. /// ## Requirements /// * The `DATABASE_URL` environment variable must be set at build-time to point to a database -/// server with the schema that the query string will be checked against. All variants of `query!()` -/// use [dotenv]1 so this can be in a `.env` file instead. +/// server with the schema that the query string will be checked against. All variants of `query!()` +/// use [dotenv]1 so this can be in a `.env` file instead. /// /// * Or, `sqlx-data.json` must exist at the workspace root. See [Offline Mode](#offline-mode-requires-the-offline-feature) /// below. diff --git a/tests/mysql/types.rs b/tests/mysql/types.rs index cd6059f2bf..8a8402a805 100644 --- a/tests/mysql/types.rs +++ b/tests/mysql/types.rs @@ -1,3 +1,4 @@ +use std::f64::consts::PI; extern crate time_ as time; #[cfg(feature = "decimal")] @@ -23,7 +24,7 @@ test_type!(i64(MySql, "2141512" == 2141512_i64)); test_type!(f64( MySql, - "3.14159265e0" == 3.14159265_f64, + "3.141592653589793e0" == PI, "25.25" == 25.25_f64, )); @@ -331,7 +332,7 @@ CREATE TEMPORARY TABLE with_bits ( // as bool let row = conn.fetch_one("SELECT value_1 FROM with_bits").await?; let v1: bool = row.try_get(0)?; - assert_eq!(v1, true); + assert!(v1); // switch the bit sqlx_oldapi::query("UPDATE with_bits SET value_1 = NOT value_1") @@ -340,7 +341,7 @@ CREATE TEMPORARY TABLE with_bits ( let row = conn.fetch_one("SELECT value_1 FROM with_bits").await?; let v1: bool = row.try_get(0)?; - assert_eq!(v1, false); + assert!(!v1); Ok(()) } diff --git a/tests/sqlite/types.rs b/tests/sqlite/types.rs index 3dad6d8f29..d84c10043a 100644 --- a/tests/sqlite/types.rs +++ b/tests/sqlite/types.rs @@ -1,4 +1,3 @@ -#![allow(clippy::approx_constant)] extern crate time_ as time; use sqlx_core::row::Row;