diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index cc0122c9..86367604 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -17,6 +17,8 @@ #![allow(clippy::needless_doctest_main, clippy::type_complexity)] // See `clippy.toml` at the workspace root #![deny(clippy::disallowed_methods)] +#![deny(clippy::cast_possible_truncation)] +#![deny(clippy::cast_possible_wrap)] // The only unsafe code in SQLx is that necessary to interact with native APIs like with SQLite, // and that can live in its own separate driver crate. #![forbid(unsafe_code)] diff --git a/sqlx-postgres/Cargo.toml b/sqlx-postgres/Cargo.toml index 13dac258..6534592d 100644 --- a/sqlx-postgres/Cargo.toml +++ b/sqlx-postgres/Cargo.toml @@ -15,9 +15,15 @@ json = ["sqlx-core/json"] migrate = ["sqlx-core/migrate"] offline = ["sqlx-core/offline"] -# Type integration features which require additional dependencies -rust_decimal = ["dep:rust_decimal", "rust_decimal/maths"] -bigdecimal = ["dep:bigdecimal", "dep:num-bigint"] +# Type Integration features +bigdecimal = ["dep:bigdecimal", "dep:num-bigint", "sqlx-core/bigdecimal"] +bit-vec = ["dep:bit-vec", "sqlx-core/bit-vec"] +chrono = ["dep:chrono", "sqlx-core/chrono"] +ipnetwork = ["dep:ipnetwork", "sqlx-core/ipnetwork"] +mac_address = ["dep:mac_address", "sqlx-core/mac_address"] +rust_decimal = ["dep:rust_decimal", "rust_decimal/maths", "sqlx-core/rust_decimal"] +time = ["dep:time", "sqlx-core/time"] +uuid = ["dep:uuid", "sqlx-core/uuid"] [dependencies] # Futures crates @@ -71,8 +77,9 @@ workspace = true # We use JSON in the driver implementation itself so there's no reason not to enable it here. features = ["json"] -[dev-dependencies] -sqlx.workspace = true +[dev-dependencies.sqlx] +workspace = true +features = ["postgres", "derive"] [target.'cfg(target_os = "windows")'.dependencies] etcetera = "0.8.0" diff --git a/sqlx-postgres/src/advisory_lock.rs b/sqlx-postgres/src/advisory_lock.rs index 82191726..98274413 100644 --- a/sqlx-postgres/src/advisory_lock.rs +++ b/sqlx-postgres/src/advisory_lock.rs @@ -98,7 +98,6 @@ impl PgAdvisoryLock { /// [hkdf]: https://datatracker.ietf.org/doc/html/rfc5869 /// ### Example /// ```rust - /// # extern crate sqlx_core as sqlx; /// use sqlx::postgres::{PgAdvisoryLock, PgAdvisoryLockKey}; /// /// let lock = PgAdvisoryLock::new("my first Postgres advisory lock!"); diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index 82bba18f..a579f921 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -2,17 +2,17 @@ use crate::error::Error; use crate::ext::ustr::UStr; use crate::message::{ParameterDescription, RowDescription}; use crate::query_as::query_as; -use crate::query_scalar::{query_scalar, query_scalar_with}; +use crate::query_scalar::{query_scalar}; use crate::statement::PgStatementMetadata; use crate::type_info::{PgArrayOf, PgCustomType, PgType, PgTypeKind}; use crate::types::Json; use crate::types::Oid; use crate::HashMap; -use crate::{PgArguments, PgColumn, PgConnection, PgTypeInfo}; +use crate::{PgColumn, PgConnection, PgTypeInfo}; use futures_core::future::BoxFuture; use smallvec::SmallVec; -use std::fmt::Write; use std::sync::Arc; +use sqlx_core::query_builder::QueryBuilder; /// Describes the type of the `pg_type.typtype` column /// @@ -423,29 +423,34 @@ WHERE rngtypid = $1 return Ok(vec![]); } - let mut nullable_query = String::from("SELECT NOT pg_attribute.attnotnull FROM (VALUES "); - let mut args = PgArguments::default(); - - for (i, (column, bind)) in meta.columns.iter().zip((1..).step_by(3)).enumerate() { - if !args.buffer.is_empty() { - nullable_query += ", "; - } - - let _ = write!( - nullable_query, - "(${}::int4, ${}::int4, ${}::int2)", - bind, - bind + 1, - bind + 2 + if meta.columns.len() * 3 > 65535 { + tracing::debug!( + ?stmt_id, + num_columns=meta.columns.len(), + "number of columns in query is too large to pull nullability for" ); - - args.add(i as i32).map_err(Error::Encode)?; - args.add(column.relation_id).map_err(Error::Encode)?; - args.add(column.relation_attribute_no) - .map_err(Error::Encode)?; } - nullable_query.push_str( + // Query for NOT NULL constraints for each column in the query. + // + // This will include columns that don't have a `relation_id` (are not from a table); + // assuming those are a minority of columns, it's less code to _not_ work around it + // and just let Postgres return `NULL`. + let mut nullable_query = QueryBuilder::new( + "SELECT NOT pg_attribute.attnotnull FROM ( " + ); + + nullable_query.push_values( + meta.columns.iter().zip(0i32..), + |mut tuple, (column, i)| { + // ({i}::int4, {column.relation_id}::int4, {column.relation_attribute_no}::int2) + tuple.push_bind(i).push_unseparated("::int4"); + tuple.push_bind(column.relation_id).push_unseparated("::int4"); + tuple.push_bind(column.relation_attribute_no).push_bind_unseparated("::int2"); + }, + ); + + nullable_query.push( ") as col(idx, table_id, col_idx) \ LEFT JOIN pg_catalog.pg_attribute \ ON table_id IS NOT NULL \ @@ -454,7 +459,8 @@ WHERE rngtypid = $1 ORDER BY col.idx", ); - let mut nullables = query_scalar_with::<_, Option, _>(&nullable_query, args) + let mut nullables: Vec> = nullable_query + .build_query_scalar() .fetch_all(&mut *self) .await?; diff --git a/sqlx-postgres/src/lib.rs b/sqlx-postgres/src/lib.rs index c50f5306..2423acb8 100644 --- a/sqlx-postgres/src/lib.rs +++ b/sqlx-postgres/src/lib.rs @@ -1,4 +1,6 @@ //! **PostgreSQL** database driver. +#![deny(clippy::cast_possible_truncation)] +#![deny(clippy::cast_possible_wrap)] #[macro_use] extern crate sqlx_core; diff --git a/sqlx-postgres/src/listener.rs b/sqlx-postgres/src/listener.rs index f23b8149..ca4f78a2 100644 --- a/sqlx-postgres/src/listener.rs +++ b/sqlx-postgres/src/listener.rs @@ -188,19 +188,17 @@ impl PgListener { /// # Example /// /// ```rust,no_run - /// # use sqlx_core::postgres::PgListener; - /// # use sqlx_core::error::Error; + /// # use sqlx::postgres::PgListener; /// # - /// # #[cfg(feature = "_rt")] /// # sqlx::__rt::test_block_on(async move { - /// # let mut listener = PgListener::connect("postgres:// ...").await?; + /// let mut listener = PgListener::connect("postgres:// ...").await?; /// loop { /// // ask for next notification, re-connecting (transparently) if needed /// let notification = listener.recv().await?; /// /// // handle notification, do something interesting /// } - /// # Result::<(), Error>::Ok(()) + /// # Result::<(), sqlx::Error>::Ok(()) /// # }).unwrap(); /// ``` pub async fn recv(&mut self) -> Result { @@ -219,10 +217,8 @@ impl PgListener { /// # Example /// /// ```rust,no_run - /// # use sqlx_core::postgres::PgListener; - /// # use sqlx_core::error::Error; + /// # use sqlx::postgres::PgListener; /// # - /// # #[cfg(feature = "_rt")] /// # sqlx::__rt::test_block_on(async move { /// # let mut listener = PgListener::connect("postgres:// ...").await?; /// loop { @@ -233,7 +229,7 @@ impl PgListener { /// /// // connection lost, do something interesting /// } - /// # Result::<(), Error>::Ok(()) + /// # Result::<(), sqlx::Error>::Ok(()) /// # }).unwrap(); /// ``` pub async fn try_recv(&mut self) -> Result, Error> { diff --git a/sqlx-postgres/src/migrate.rs b/sqlx-postgres/src/migrate.rs index 5e62a628..da308058 100644 --- a/sqlx-postgres/src/migrate.rs +++ b/sqlx-postgres/src/migrate.rs @@ -230,6 +230,7 @@ CREATE TABLE IF NOT EXISTS _sqlx_migrations ( let elapsed = start.elapsed(); // language=SQL + #[allow(clippy::cast_possible_truncation)] let _ = query( r#" UPDATE _sqlx_migrations diff --git a/sqlx-postgres/src/options/mod.rs b/sqlx-postgres/src/options/mod.rs index b99edf67..a0b22260 100644 --- a/sqlx-postgres/src/options/mod.rs +++ b/sqlx-postgres/src/options/mod.rs @@ -82,7 +82,8 @@ mod ssl_mode; /// // Information about SQL queries is logged at `DEBUG` level by default. /// opts = opts.log_statements(log::LevelFilter::Trace); /// -/// let pool = PgPool::connect_with(&opts).await?; +/// let pool = PgPool::connect_with(opts).await?; +/// # Ok(()) /// # } /// ``` #[derive(Debug, Clone)] diff --git a/sqlx-postgres/src/type_info.rs b/sqlx-postgres/src/type_info.rs index f50ea7fb..3d948f73 100644 --- a/sqlx-postgres/src/type_info.rs +++ b/sqlx-postgres/src/type_info.rs @@ -294,7 +294,7 @@ impl PgTypeInfo { /// in quotes, e.g.: /// ``` /// use sqlx::postgres::PgTypeInfo; - /// use sqlx::Type; + /// use sqlx::{Type, TypeInfo}; /// /// /// `CREATE TYPE "_foo" AS ENUM ('Bar', 'Baz');` /// #[derive(sqlx::Type)]