diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 641a42722..3426d624b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,16 +73,18 @@ jobs: path: target key: check-wasm32-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }} - run: cargo check --target wasm32-unknown-unknown --manifest-path tokio-postgres/Cargo.toml --no-default-features --features js + env: + RUSTFLAGS: --cfg getrandom_backend="wasm_js" test: name: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: docker compose up -d - uses: sfackler/actions/rustup@master with: - version: 1.74.0 + version: 1.81.0 - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - uses: actions/cache@v3 diff --git a/docker-compose.yml b/docker-compose.yml index 0ed44148d..991df2d01 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '2' services: postgres: - image: postgres:14 + image: docker.io/postgres:17 ports: - 5433:5433 volumes: diff --git a/postgres-native-tls/CHANGELOG.md b/postgres-native-tls/CHANGELOG.md index 9eb7ab800..5fe0a9c7a 100644 --- a/postgres-native-tls/CHANGELOG.md +++ b/postgres-native-tls/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.5.1 - 2025-02-02 + +### Added + +* Added `set_postgresql_alpn`. + ## v0.5.0 - 2020-12-25 ### Changed diff --git a/postgres-native-tls/Cargo.toml b/postgres-native-tls/Cargo.toml index 63b7ea957..318f3b1bf 100644 --- a/postgres-native-tls/Cargo.toml +++ b/postgres-native-tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-native-tls" -version = "0.5.0" +version = "0.5.1" authors = ["Steven Fackler "] edition = "2018" license = "MIT OR Apache-2.0" @@ -17,7 +17,7 @@ runtime = ["tokio-postgres/runtime"] vendored-openssl = ["tokio-native-tls/vendored", "native-tls/vendored"] [dependencies] -native-tls = "0.2" +native-tls = { version = "0.2", features = ["alpn"] } tokio = "1.0" tokio-native-tls = "0.3" tokio-postgres = { version = "0.7.11", path = "../tokio-postgres", default-features = false } diff --git a/postgres-native-tls/src/lib.rs b/postgres-native-tls/src/lib.rs index a06f185b5..9ee7da653 100644 --- a/postgres-native-tls/src/lib.rs +++ b/postgres-native-tls/src/lib.rs @@ -53,6 +53,7 @@ //! ``` #![warn(rust_2018_idioms, clippy::all, missing_docs)] +use native_tls::TlsConnectorBuilder; use std::future::Future; use std::io; use std::pin::Pin; @@ -180,3 +181,10 @@ where } } } + +/// Set ALPN for `TlsConnectorBuilder` +/// +/// This is required when using `sslnegotiation=direct` +pub fn set_postgresql_alpn(builder: &mut TlsConnectorBuilder) { + builder.request_alpns(&["postgresql"]); +} diff --git a/postgres-native-tls/src/test.rs b/postgres-native-tls/src/test.rs index 25cc6fdbd..738c04bd7 100644 --- a/postgres-native-tls/src/test.rs +++ b/postgres-native-tls/src/test.rs @@ -5,7 +5,7 @@ use tokio_postgres::tls::TlsConnect; #[cfg(feature = "runtime")] use crate::MakeTlsConnector; -use crate::TlsConnector; +use crate::{set_postgresql_alpn, TlsConnector}; async fn smoke_test(s: &str, tls: T) where @@ -42,6 +42,21 @@ async fn require() { .await; } +#[tokio::test] +async fn direct() { + let mut builder = native_tls::TlsConnector::builder(); + builder.add_root_certificate( + Certificate::from_pem(include_bytes!("../../test/server.crt")).unwrap(), + ); + set_postgresql_alpn(&mut builder); + let connector = builder.build().unwrap(); + smoke_test( + "user=ssl_user dbname=postgres sslmode=require sslnegotiation=direct", + TlsConnector::new(connector, "localhost"), + ) + .await; +} + #[tokio::test] async fn prefer() { let connector = native_tls::TlsConnector::builder() diff --git a/postgres-openssl/CHANGELOG.md b/postgres-openssl/CHANGELOG.md index 346214ae8..33f5a127a 100644 --- a/postgres-openssl/CHANGELOG.md +++ b/postgres-openssl/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.5.1 - 2025-02-02 + +### Added + +* Added `set_postgresql_alpn`. + ## v0.5.0 - 2020-12-25 ### Changed diff --git a/postgres-openssl/Cargo.toml b/postgres-openssl/Cargo.toml index 9013384a2..6ebb86bef 100644 --- a/postgres-openssl/Cargo.toml +++ b/postgres-openssl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-openssl" -version = "0.5.0" +version = "0.5.1" authors = ["Steven Fackler "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/postgres-openssl/src/lib.rs b/postgres-openssl/src/lib.rs index 837663fe7..232cccd05 100644 --- a/postgres-openssl/src/lib.rs +++ b/postgres-openssl/src/lib.rs @@ -53,7 +53,7 @@ use openssl::hash::MessageDigest; use openssl::nid::Nid; #[cfg(feature = "runtime")] use openssl::ssl::SslConnector; -use openssl::ssl::{self, ConnectConfiguration, SslRef}; +use openssl::ssl::{self, ConnectConfiguration, SslConnectorBuilder, SslRef}; use openssl::x509::X509VerifyResult; use std::error::Error; use std::fmt::{self, Debug}; @@ -250,3 +250,10 @@ fn tls_server_end_point(ssl: &SslRef) -> Option> { }; cert.digest(md).ok().map(|b| b.to_vec()) } + +/// Set ALPN for `SslConnectorBuilder` +/// +/// This is required when using `sslnegotiation=direct` +pub fn set_postgresql_alpn(builder: &mut SslConnectorBuilder) -> Result<(), ErrorStack> { + builder.set_alpn_protos(b"\x0apostgresql") +} diff --git a/postgres-openssl/src/test.rs b/postgres-openssl/src/test.rs index b361ee446..66bb22641 100644 --- a/postgres-openssl/src/test.rs +++ b/postgres-openssl/src/test.rs @@ -37,6 +37,19 @@ async fn require() { .await; } +#[tokio::test] +async fn direct() { + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_ca_file("../test/server.crt").unwrap(); + set_postgresql_alpn(&mut builder).unwrap(); + let ctx = builder.build(); + smoke_test( + "user=ssl_user dbname=postgres sslmode=require sslnegotiation=direct", + TlsConnector::new(ctx.configure().unwrap(), "localhost"), + ) + .await; +} + #[tokio::test] async fn prefer() { let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); diff --git a/postgres-protocol/CHANGELOG.md b/postgres-protocol/CHANGELOG.md index 54dce91b0..25e717128 100644 --- a/postgres-protocol/CHANGELOG.md +++ b/postgres-protocol/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.6.8 - 2025-02-02 + +### Changed + +* Upgraded `getrandom`. + ## v0.6.7 - 2024-07-21 ### Deprecated diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index 49cf2d59c..9351ea14f 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-protocol" -version = "0.6.7" +version = "0.6.8" authors = ["Steven Fackler "] edition = "2018" description = "Low level Postgres protocol APIs" @@ -10,7 +10,7 @@ readme = "../README.md" [features] default = [] -js = ["getrandom/js"] +js = ["getrandom/wasm_js"] [dependencies] base64 = "0.22" @@ -20,7 +20,7 @@ fallible-iterator = "0.2" hmac = "0.12" md-5 = "0.10" memchr = "2.0" -rand = "0.8" +rand = "0.9" sha2 = "0.10" stringprep = "0.1" -getrandom = { version = "0.2", optional = true } +getrandom = { version = "0.3", optional = true } diff --git a/postgres-protocol/src/authentication/sasl.rs b/postgres-protocol/src/authentication/sasl.rs index 4a77507e9..ccd40e8d0 100644 --- a/postgres-protocol/src/authentication/sasl.rs +++ b/postgres-protocol/src/authentication/sasl.rs @@ -136,10 +136,10 @@ impl ScramSha256 { /// Constructs a new instance which will use the provided password for authentication. pub fn new(password: &[u8], channel_binding: ChannelBinding) -> ScramSha256 { // rand 0.5's ThreadRng is cryptographically secure - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let nonce = (0..NONCE_LENGTH) .map(|_| { - let mut v = rng.gen_range(0x21u8..0x7e); + let mut v = rng.random_range(0x21u8..0x7e); if v == 0x2c { v = 0x7e } diff --git a/postgres-protocol/src/message/backend.rs b/postgres-protocol/src/message/backend.rs index 73b169288..013bfbb81 100644 --- a/postgres-protocol/src/message/backend.rs +++ b/postgres-protocol/src/message/backend.rs @@ -475,7 +475,7 @@ pub struct ColumnFormats<'a> { remaining: u16, } -impl<'a> FallibleIterator for ColumnFormats<'a> { +impl FallibleIterator for ColumnFormats<'_> { type Item = u16; type Error = io::Error; @@ -557,7 +557,7 @@ pub struct DataRowRanges<'a> { remaining: u16, } -impl<'a> FallibleIterator for DataRowRanges<'a> { +impl FallibleIterator for DataRowRanges<'_> { type Item = Option>; type Error = io::Error; @@ -645,7 +645,7 @@ pub struct ErrorField<'a> { value: &'a [u8], } -impl<'a> ErrorField<'a> { +impl ErrorField<'_> { #[inline] pub fn type_(&self) -> u8 { self.type_ @@ -717,7 +717,7 @@ pub struct Parameters<'a> { remaining: u16, } -impl<'a> FallibleIterator for Parameters<'a> { +impl FallibleIterator for Parameters<'_> { type Item = Oid; type Error = io::Error; diff --git a/postgres-protocol/src/password/mod.rs b/postgres-protocol/src/password/mod.rs index f03bb811d..445fb0c0e 100644 --- a/postgres-protocol/src/password/mod.rs +++ b/postgres-protocol/src/password/mod.rs @@ -28,7 +28,7 @@ const SCRAM_DEFAULT_SALT_LEN: usize = 16; /// special characters that would require escaping in an SQL command. pub fn scram_sha_256(password: &[u8]) -> String { let mut salt: [u8; SCRAM_DEFAULT_SALT_LEN] = [0; SCRAM_DEFAULT_SALT_LEN]; - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); rng.fill_bytes(&mut salt); scram_sha_256_salt(password, salt) } diff --git a/postgres-protocol/src/types/mod.rs b/postgres-protocol/src/types/mod.rs index 05f515f76..37dc793b1 100644 --- a/postgres-protocol/src/types/mod.rs +++ b/postgres-protocol/src/types/mod.rs @@ -582,7 +582,7 @@ impl<'a> Array<'a> { /// An iterator over the dimensions of an array. pub struct ArrayDimensions<'a>(&'a [u8]); -impl<'a> FallibleIterator for ArrayDimensions<'a> { +impl FallibleIterator for ArrayDimensions<'_> { type Item = ArrayDimension; type Error = StdBox; @@ -950,7 +950,7 @@ pub struct PathPoints<'a> { buf: &'a [u8], } -impl<'a> FallibleIterator for PathPoints<'a> { +impl FallibleIterator for PathPoints<'_> { type Item = Point; type Error = StdBox; diff --git a/postgres-types/CHANGELOG.md b/postgres-types/CHANGELOG.md index 777f999a7..7fa6d6506 100644 --- a/postgres-types/CHANGELOG.md +++ b/postgres-types/CHANGELOG.md @@ -2,6 +2,16 @@ ## Unreleased +## v0.2.9 - 2025-02-02 + +### Added + +* Added support for `cidr` 0.3 via the `with-cidr-0_3` feature. + +### Fixed + +* Fixed deserialization of out of bounds inputs to `time` 0.3 types to return an error rather than panic. + ## v0.2.8 - 2024-09-15 ### Added diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index ec8cea4dc..8d56f99ec 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-types" -version = "0.2.8" +version = "0.2.9" authors = ["Steven Fackler "] edition = "2018" license = "MIT OR Apache-2.0" @@ -16,6 +16,7 @@ array-impls = ["array-init"] js = ["postgres-protocol/js"] with-bit-vec-0_6 = ["bit-vec-06"] with-cidr-0_2 = ["cidr-02"] +with-cidr-0_3 = ["cidr-03"] with-chrono-0_4 = ["chrono-04"] with-eui48-0_4 = ["eui48-04"] with-eui48-1 = ["eui48-1"] @@ -32,7 +33,7 @@ with-time-0_3 = ["time-03"] [dependencies] bytes = "1.0" fallible-iterator = "0.2" -postgres-protocol = { version = "0.6.7", path = "../postgres-protocol" } +postgres-protocol = { version = "0.6.8", path = "../postgres-protocol" } postgres-derive = { version = "0.4.6", optional = true, path = "../postgres-derive" } array-init = { version = "2", optional = true } @@ -41,6 +42,7 @@ chrono-04 = { version = "0.4.16", package = "chrono", default-features = false, "clock", ], optional = true } cidr-02 = { version = "0.2", package = "cidr", optional = true } +cidr-03 = { version = "0.3", package = "cidr", optional = true } # eui48-04 will stop compiling and support will be removed # See https://github.com/sfackler/rust-postgres/issues/1073 eui48-04 = { version = "0.4", package = "eui48", optional = true } diff --git a/postgres-types/src/cidr_03.rs b/postgres-types/src/cidr_03.rs new file mode 100644 index 000000000..6a0178711 --- /dev/null +++ b/postgres-types/src/cidr_03.rs @@ -0,0 +1,44 @@ +use bytes::BytesMut; +use cidr_03::{IpCidr, IpInet}; +use postgres_protocol::types; +use std::error::Error; + +use crate::{FromSql, IsNull, ToSql, Type}; + +impl<'a> FromSql<'a> for IpCidr { + fn from_sql(_: &Type, raw: &[u8]) -> Result> { + let inet = types::inet_from_sql(raw)?; + Ok(IpCidr::new(inet.addr(), inet.netmask())?) + } + + accepts!(CIDR); +} + +impl ToSql for IpCidr { + fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { + types::inet_to_sql(self.first_address(), self.network_length(), w); + Ok(IsNull::No) + } + + accepts!(CIDR); + to_sql_checked!(); +} + +impl<'a> FromSql<'a> for IpInet { + fn from_sql(_: &Type, raw: &[u8]) -> Result> { + let inet = types::inet_from_sql(raw)?; + Ok(IpInet::new(inet.addr(), inet.netmask())?) + } + + accepts!(INET); +} + +impl ToSql for IpInet { + fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { + types::inet_to_sql(self.address(), self.network_length(), w); + Ok(IsNull::No) + } + + accepts!(INET); + to_sql_checked!(); +} diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index c56eebba6..8c54cdc6a 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -268,6 +268,8 @@ mod bit_vec_06; mod chrono_04; #[cfg(feature = "with-cidr-0_2")] mod cidr_02; +#[cfg(feature = "with-cidr-0_3")] +mod cidr_03; #[cfg(feature = "with-eui48-0_4")] mod eui48_04; #[cfg(feature = "with-eui48-1")] @@ -313,23 +315,14 @@ impl fmt::Debug for Type { impl fmt::Display for Type { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.schema() { - "public" | "pg_catalog" => {} - schema => write!(fmt, "{}.", schema)?, - } fmt.write_str(self.name()) } } impl Type { /// Creates a new `Type`. - pub fn new(name: String, oid: Oid, kind: Kind, schema: String) -> Type { - Type(Inner::Other(Arc::new(Other { - name, - oid, - kind, - schema, - }))) + pub fn new(name: String, oid: Oid, kind: Kind) -> Type { + Type(Inner::Other(Arc::new(Other { name, oid, kind }))) } /// Returns the `Type` corresponding to the provided `Oid` if it @@ -348,14 +341,6 @@ impl Type { self.0.kind() } - /// Returns the schema of this type. - pub fn schema(&self) -> &str { - match self.0 { - Inner::Other(ref u) => &u.schema, - _ => "pg_catalog", - } - } - /// Returns the name of this type. pub fn name(&self) -> &str { self.0.name() @@ -489,6 +474,8 @@ impl WrongType { /// | `chrono::DateTime` | TIMESTAMP WITH TIME ZONE | /// | `chrono::NaiveDate` | DATE | /// | `chrono::NaiveTime` | TIME | +/// | `cidr::IpCidr` | CIDR | +/// | `cidr::IpInet` | INET | /// | `time::PrimitiveDateTime` | TIMESTAMP | /// | `time::OffsetDateTime` | TIMESTAMP WITH TIME ZONE | /// | `time::Date` | DATE | @@ -840,6 +827,8 @@ pub enum IsNull { /// | `chrono::DateTime` | TIMESTAMP WITH TIME ZONE | /// | `chrono::NaiveDate` | DATE | /// | `chrono::NaiveTime` | TIME | +/// | `cidr::IpCidr` | CIDR | +/// | `cidr::IpInet` | INET | /// | `time::PrimitiveDateTime` | TIMESTAMP | /// | `time::OffsetDateTime` | TIMESTAMP WITH TIME ZONE | /// | `time::Date` | DATE | @@ -914,7 +903,7 @@ pub enum Format { Binary, } -impl<'a, T> ToSql for &'a T +impl ToSql for &T where T: ToSql, { @@ -955,7 +944,7 @@ impl ToSql for Option { fn encode_format(&self, ty: &Type) -> Format { match self { - Some(ref val) => val.encode_format(ty), + Some(val) => val.encode_format(ty), None => Format::Binary, } } @@ -963,7 +952,7 @@ impl ToSql for Option { to_sql_checked!(); } -impl<'a, T: ToSql> ToSql for &'a [T] { +impl ToSql for &[T] { fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { let member_type = match *ty.kind() { Kind::Array(ref member) => member, @@ -1004,7 +993,7 @@ impl<'a, T: ToSql> ToSql for &'a [T] { to_sql_checked!(); } -impl<'a> ToSql for &'a [u8] { +impl ToSql for &[u8] { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { types::bytea_to_sql(self, w); Ok(IsNull::No) @@ -1064,7 +1053,7 @@ impl ToSql for Box<[T]> { to_sql_checked!(); } -impl<'a> ToSql for Cow<'a, [u8]> { +impl ToSql for Cow<'_, [u8]> { fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { <&[u8] as ToSql>::to_sql(&self.as_ref(), ty, w) } @@ -1088,7 +1077,7 @@ impl ToSql for Vec { to_sql_checked!(); } -impl<'a> ToSql for &'a str { +impl ToSql for &str { fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { match ty.name() { "ltree" => types::ltree_to_sql(self, w), @@ -1109,7 +1098,7 @@ impl<'a> ToSql for &'a str { to_sql_checked!(); } -impl<'a> ToSql for Cow<'a, str> { +impl ToSql for Cow<'_, str> { fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { <&str as ToSql>::to_sql(&self.as_ref(), ty, w) } @@ -1256,17 +1245,17 @@ impl BorrowToSql for &dyn ToSql { } } -impl<'a> sealed::Sealed for Box {} +impl sealed::Sealed for Box {} -impl<'a> BorrowToSql for Box { +impl BorrowToSql for Box { #[inline] fn borrow_to_sql(&self) -> &dyn ToSql { self.as_ref() } } -impl<'a> sealed::Sealed for Box {} -impl<'a> BorrowToSql for Box { +impl sealed::Sealed for Box {} +impl BorrowToSql for Box { #[inline] fn borrow_to_sql(&self) -> &dyn ToSql { self.as_ref() diff --git a/postgres-types/src/time_03.rs b/postgres-types/src/time_03.rs index f136fab7c..4deea663f 100644 --- a/postgres-types/src/time_03.rs +++ b/postgres-types/src/time_03.rs @@ -13,7 +13,9 @@ fn base() -> PrimitiveDateTime { impl<'a> FromSql<'a> for PrimitiveDateTime { fn from_sql(_: &Type, raw: &[u8]) -> Result> { let t = types::timestamp_from_sql(raw)?; - Ok(base() + Duration::microseconds(t)) + Ok(base() + .checked_add(Duration::microseconds(t)) + .ok_or("value too large to decode")?) } accepts!(TIMESTAMP); @@ -62,7 +64,10 @@ impl ToSql for OffsetDateTime { impl<'a> FromSql<'a> for Date { fn from_sql(_: &Type, raw: &[u8]) -> Result> { let jd = types::date_from_sql(raw)?; - Ok(base().date() + Duration::days(i64::from(jd))) + Ok(base() + .date() + .checked_add(Duration::days(i64::from(jd))) + .ok_or("value too large to decode")?) } accepts!(DATE); diff --git a/postgres-types/src/type_gen.rs b/postgres-types/src/type_gen.rs index a1bc3f85c..311dbdb96 100644 --- a/postgres-types/src/type_gen.rs +++ b/postgres-types/src/type_gen.rs @@ -8,7 +8,6 @@ pub struct Other { pub name: String, pub oid: Oid, pub kind: Kind, - pub schema: String, } #[derive(PartialEq, Eq, Clone, Debug, Hash)] diff --git a/postgres/CHANGELOG.md b/postgres/CHANGELOG.md index 6feb629e4..c3e686714 100644 --- a/postgres/CHANGELOG.md +++ b/postgres/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## v0.19.9 - 2024-09-15 + ### Added * Added support for `jiff` 0.1 via the `with-jiff-01` feature. diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index e0e580f7d..cd7b164b1 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres" -version = "0.19.8" +version = "0.19.9" authors = ["Steven Fackler "] edition = "2018" license = "MIT OR Apache-2.0" @@ -24,6 +24,8 @@ circle-ci = { repository = "sfackler/rust-postgres" } array-impls = ["tokio-postgres/array-impls"] with-bit-vec-0_6 = ["tokio-postgres/with-bit-vec-0_6"] with-chrono-0_4 = ["tokio-postgres/with-chrono-0_4"] +with-cidr-0_2 = ["tokio-postgres/with-cidr-0_2"] +with-cidr-0_3 = ["tokio-postgres/with-cidr-0_3"] with-eui48-0_4 = ["tokio-postgres/with-eui48-0_4"] with-eui48-1 = ["tokio-postgres/with-eui48-1"] with-geo-types-0_6 = ["tokio-postgres/with-geo-types-0_6"] @@ -41,7 +43,7 @@ bytes = "1.0" fallible-iterator = "0.2" futures-util = { version = "0.3.14", features = ["sink"] } log = "0.4" -tokio-postgres = { version = "0.7.11", path = "../tokio-postgres" } +tokio-postgres = { version = "0.7.13", path = "../tokio-postgres" } tokio = { version = "1.0", features = ["rt", "time"] } [dev-dependencies] diff --git a/postgres/src/config.rs b/postgres/src/config.rs index cb67664ca..94918c753 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -12,7 +12,7 @@ use std::time::Duration; use tokio::runtime; #[doc(inline)] pub use tokio_postgres::config::{ - ChannelBinding, Host, LoadBalanceHosts, SslMode, TargetSessionAttrs, + ChannelBinding, Host, LoadBalanceHosts, SslMode, SslNegotiation, TargetSessionAttrs, }; use tokio_postgres::error::DbError; use tokio_postgres::tls::{MakeTlsConnect, TlsConnect}; @@ -40,6 +40,9 @@ use tokio_postgres::{Error, Socket}; /// path to the directory containing Unix domain sockets. Otherwise, it is treated as a hostname. Multiple hosts /// can be specified, separated by commas. Each host will be tried in turn when connecting. Required if connecting /// with the `connect` method. +/// * `sslnegotiation` - TLS negotiation method. If set to `direct`, the client will perform direct TLS handshake, this only works for PostgreSQL 17 and newer. +/// Note that you will need to setup ALPN of TLS client configuration to `postgresql` when using direct TLS. +/// If set to `postgres`, the default value, it follows original postgres wire protocol to perform the negotiation. /// * `hostaddr` - Numeric IP address of host to connect to. This should be in the standard IPv4 address format, /// e.g., 172.28.40.9. If your machine supports IPv6, you can also use those addresses. /// If this parameter is not specified, the value of `host` will be looked up to find the corresponding IP address, @@ -230,6 +233,17 @@ impl Config { self.config.get_ssl_mode() } + /// Sets the SSL negotiation method + pub fn ssl_negotiation(&mut self, ssl_negotiation: SslNegotiation) -> &mut Config { + self.config.ssl_negotiation(ssl_negotiation); + self + } + + /// Gets the SSL negotiation method + pub fn get_ssl_negotiation(&self) -> SslNegotiation { + self.config.get_ssl_negotiation() + } + /// Adds a host to the configuration. /// /// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix diff --git a/postgres/src/notifications.rs b/postgres/src/notifications.rs index c31d4f631..0c040dedf 100644 --- a/postgres/src/notifications.rs +++ b/postgres/src/notifications.rs @@ -77,7 +77,7 @@ pub struct Iter<'a> { connection: ConnectionRef<'a>, } -impl<'a> FallibleIterator for Iter<'a> { +impl FallibleIterator for Iter<'_> { type Item = Notification; type Error = Error; @@ -100,7 +100,7 @@ pub struct BlockingIter<'a> { connection: ConnectionRef<'a>, } -impl<'a> FallibleIterator for BlockingIter<'a> { +impl FallibleIterator for BlockingIter<'_> { type Item = Notification; type Error = Error; @@ -129,7 +129,7 @@ pub struct TimeoutIter<'a> { timeout: Duration, } -impl<'a> FallibleIterator for TimeoutIter<'a> { +impl FallibleIterator for TimeoutIter<'_> { type Item = Notification; type Error = Error; diff --git a/postgres/src/transaction.rs b/postgres/src/transaction.rs index 5c8c15973..8126b1dbe 100644 --- a/postgres/src/transaction.rs +++ b/postgres/src/transaction.rs @@ -12,7 +12,7 @@ pub struct Transaction<'a> { transaction: Option>, } -impl<'a> Drop for Transaction<'a> { +impl Drop for Transaction<'_> { fn drop(&mut self) { if let Some(transaction) = self.transaction.take() { let _ = self.connection.block_on(transaction.rollback()); diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index 9ee470db6..a67f69ea7 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -2,6 +2,21 @@ ## Unreleased +## v0.7.13 - 2025-02-02 + +### Added + +* Added support for direct TLS negotiation. +* Added support for `cidr` 0.3 via the `with-cidr-0_3` feature. + +### Fixes + +* Added `load_balance_hosts` to `Config`'s `Debug` implementation. + +### Changes + +* Upgraded `rand`. + ## v0.7.12 - 2024-09-15 ### Fixed diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 94e6b8d4f..d619c19ce 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-postgres" -version = "0.7.12" +version = "0.7.13" authors = ["Steven Fackler "] edition = "2018" license = "MIT OR Apache-2.0" @@ -30,6 +30,8 @@ runtime = ["tokio/net", "tokio/time"] array-impls = ["postgres-types/array-impls"] with-bit-vec-0_6 = ["postgres-types/with-bit-vec-0_6"] with-chrono-0_4 = ["postgres-types/with-chrono-0_4"] +with-cidr-0_2 = ["postgres-types/with-cidr-0_2"] +with-cidr-0_3 = ["postgres-types/with-cidr-0_3"] with-eui48-0_4 = ["postgres-types/with-eui48-0_4"] with-eui48-1 = ["postgres-types/with-eui48-1"] with-geo-types-0_6 = ["postgres-types/with-geo-types-0_6"] @@ -55,12 +57,12 @@ parking_lot = "0.12" percent-encoding = "2.0" pin-project-lite = "0.2" phf = "0.11" -postgres-protocol = { version = "0.6.7", path = "../postgres-protocol" } -postgres-types = { version = "0.2.8", path = "../postgres-types" } +postgres-protocol = { version = "0.6.8", path = "../postgres-protocol" } +postgres-types = { version = "0.2.9", path = "../postgres-types" } socket2 = "0.5.3" -tokio = { version = "1.27", features = ["io-util"] } +tokio = { version = "1.27", features = ["io-util", "rt"] } tokio-util = { version = "0.7", features = ["codec"] } -rand = "0.8.5" +rand = "0.9.0" whoami = "1.4.1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/tokio-postgres/src/cancel_query.rs b/tokio-postgres/src/cancel_query.rs index 078d4b8b6..2dfd47c06 100644 --- a/tokio-postgres/src/cancel_query.rs +++ b/tokio-postgres/src/cancel_query.rs @@ -1,5 +1,5 @@ use crate::client::SocketConfig; -use crate::config::SslMode; +use crate::config::{SslMode, SslNegotiation}; use crate::tls::MakeTlsConnect; use crate::{cancel_query_raw, connect_socket, Error, Socket}; use std::io; @@ -7,6 +7,7 @@ use std::io; pub(crate) async fn cancel_query( config: Option, ssl_mode: SslMode, + ssl_negotiation: SslNegotiation, mut tls: T, process_id: i32, secret_key: i32, @@ -38,6 +39,14 @@ where ) .await?; - cancel_query_raw::cancel_query_raw(socket, ssl_mode, tls, has_hostname, process_id, secret_key) - .await + cancel_query_raw::cancel_query_raw( + socket, + ssl_mode, + ssl_negotiation, + tls, + has_hostname, + process_id, + secret_key, + ) + .await } diff --git a/tokio-postgres/src/cancel_query_raw.rs b/tokio-postgres/src/cancel_query_raw.rs index 41aafe7d9..886606497 100644 --- a/tokio-postgres/src/cancel_query_raw.rs +++ b/tokio-postgres/src/cancel_query_raw.rs @@ -1,4 +1,4 @@ -use crate::config::SslMode; +use crate::config::{SslMode, SslNegotiation}; use crate::tls::TlsConnect; use crate::{connect_tls, Error}; use bytes::BytesMut; @@ -8,6 +8,7 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; pub async fn cancel_query_raw( stream: S, mode: SslMode, + negotiation: SslNegotiation, tls: T, has_hostname: bool, process_id: i32, @@ -17,7 +18,7 @@ where S: AsyncRead + AsyncWrite + Unpin, T: TlsConnect, { - let mut stream = connect_tls::connect_tls(stream, mode, tls, has_hostname).await?; + let mut stream = connect_tls::connect_tls(stream, mode, negotiation, tls, has_hostname).await?; let mut buf = BytesMut::new(); frontend::cancel_request(process_id, secret_key, &mut buf); diff --git a/tokio-postgres/src/cancel_token.rs b/tokio-postgres/src/cancel_token.rs index c925ce0ca..1652bec72 100644 --- a/tokio-postgres/src/cancel_token.rs +++ b/tokio-postgres/src/cancel_token.rs @@ -1,4 +1,4 @@ -use crate::config::SslMode; +use crate::config::{SslMode, SslNegotiation}; use crate::tls::TlsConnect; #[cfg(feature = "runtime")] use crate::{cancel_query, client::SocketConfig, tls::MakeTlsConnect, Socket}; @@ -12,6 +12,7 @@ pub struct CancelToken { #[cfg(feature = "runtime")] pub(crate) socket_config: Option, pub(crate) ssl_mode: SslMode, + pub(crate) ssl_negotiation: SslNegotiation, pub(crate) process_id: i32, pub(crate) secret_key: i32, } @@ -37,6 +38,7 @@ impl CancelToken { cancel_query::cancel_query( self.socket_config.clone(), self.ssl_mode, + self.ssl_negotiation, tls, self.process_id, self.secret_key, @@ -54,6 +56,7 @@ impl CancelToken { cancel_query_raw::cancel_query_raw( stream, self.ssl_mode, + self.ssl_negotiation, tls, true, self.process_id, diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index 6c868e991..16fbdfb58 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -1,5 +1,5 @@ use crate::codec::BackendMessages; -use crate::config::SslMode; +use crate::config::{SslMode, SslNegotiation}; use crate::connection::{Request, RequestMessages}; use crate::copy_out::CopyOutStream; #[cfg(feature = "runtime")] @@ -205,6 +205,7 @@ pub struct Client { #[cfg(feature = "runtime")] socket_config: Option, ssl_mode: SslMode, + ssl_negotiation: SslNegotiation, process_id: i32, secret_key: i32, } @@ -213,6 +214,7 @@ impl Client { pub(crate) fn new( sender: mpsc::UnboundedSender, ssl_mode: SslMode, + ssl_negotiation: SslNegotiation, process_id: i32, secret_key: i32, pgbouncer_mode: bool, @@ -227,6 +229,7 @@ impl Client { #[cfg(feature = "runtime")] socket_config: None, ssl_mode, + ssl_negotiation, process_id, secret_key, } @@ -577,6 +580,7 @@ impl Client { #[cfg(feature = "runtime")] socket_config: self.socket_config.clone(), ssl_mode: self.ssl_mode, + ssl_negotiation: self.ssl_negotiation, process_id: self.process_id, secret_key: self.secret_key, } diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 716a82673..3e13b69cf 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -50,6 +50,20 @@ pub enum SslMode { Require, } +/// TLS negotiation configuration +/// +/// See more information at +/// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-SSLNEGOTIATION +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +#[non_exhaustive] +pub enum SslNegotiation { + /// Use PostgreSQL SslRequest for Ssl negotiation + #[default] + Postgres, + /// Start Ssl handshake without negotiation, only works for PostgreSQL 17+ + Direct, +} + /// Channel binding configuration. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[non_exhaustive] @@ -106,6 +120,15 @@ pub enum Host { /// path to the directory containing Unix domain sockets. Otherwise, it is treated as a hostname. Multiple hosts /// can be specified, separated by commas. Each host will be tried in turn when connecting. Required if connecting /// with the `connect` method. +/// * `sslnegotiation` - TLS negotiation method. If set to `direct`, the client +/// will perform direct TLS handshake, this only works for PostgreSQL 17 and +/// newer. +/// Note that you will need to setup ALPN of TLS client configuration to +/// `postgresql` when using direct TLS. If you are using postgres_openssl +/// as TLS backend, a `postgres_openssl::set_postgresql_alpn` helper is +/// provided for that. +/// If set to `postgres`, the default value, it follows original postgres +/// wire protocol to perform the negotiation. /// * `hostaddr` - Numeric IP address of host to connect to. This should be in the standard IPv4 address format, /// e.g., 172.28.40.9. If your machine supports IPv6, you can also use those addresses. /// If this parameter is not specified, the value of `host` will be looked up to find the corresponding IP address, @@ -198,6 +221,7 @@ pub struct Config { pub(crate) options: Option, pub(crate) application_name: Option, pub(crate) ssl_mode: SslMode, + pub(crate) ssl_negotiation: SslNegotiation, pub(crate) host: Vec, pub(crate) hostaddr: Vec, pub(crate) port: Vec, @@ -229,6 +253,7 @@ impl Config { options: None, application_name: None, ssl_mode: SslMode::Prefer, + ssl_negotiation: SslNegotiation::Postgres, host: vec![], hostaddr: vec![], port: vec![], @@ -329,6 +354,19 @@ impl Config { self.ssl_mode } + /// Sets the SSL negotiation method. + /// + /// Defaults to `postgres`. + pub fn ssl_negotiation(&mut self, ssl_negotiation: SslNegotiation) -> &mut Config { + self.ssl_negotiation = ssl_negotiation; + self + } + + /// Gets the SSL negotiation method. + pub fn get_ssl_negotiation(&self) -> SslNegotiation { + self.ssl_negotiation + } + /// Adds a host to the configuration. /// /// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix @@ -580,6 +618,18 @@ impl Config { }; self.ssl_mode(mode); } + "sslnegotiation" => { + let mode = match value { + "postgres" => SslNegotiation::Postgres, + "direct" => SslNegotiation::Direct, + _ => { + return Err(Error::config_parse(Box::new(InvalidValue( + "sslnegotiation", + )))) + } + }; + self.ssl_negotiation(mode); + } "host" => { for host in value.split(',') { self.host(host); @@ -774,6 +824,7 @@ impl fmt::Debug for Config { config_dbg .field("target_session_attrs", &self.target_session_attrs) .field("channel_binding", &self.channel_binding) + .field("load_balance_hosts", &self.load_balance_hosts) .finish() } } diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 8189cb91c..e97a7a2a3 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -44,7 +44,7 @@ where let mut indices = (0..num_hosts).collect::>(); if config.load_balance_hosts == LoadBalanceHosts::Random { - indices.shuffle(&mut rand::thread_rng()); + indices.shuffle(&mut rand::rng()); } let mut error = None; @@ -101,7 +101,7 @@ where .collect::>(); if config.load_balance_hosts == LoadBalanceHosts::Random { - addrs.shuffle(&mut rand::thread_rng()); + addrs.shuffle(&mut rand::rng()); } let mut last_err = None; diff --git a/tokio-postgres/src/connect_raw.rs b/tokio-postgres/src/connect_raw.rs index 8bd603c4e..1931691e3 100644 --- a/tokio-postgres/src/connect_raw.rs +++ b/tokio-postgres/src/connect_raw.rs @@ -89,7 +89,14 @@ where S: AsyncRead + AsyncWrite + Unpin, T: TlsConnect, { - let stream = connect_tls(stream, config.ssl_mode, tls, has_hostname).await?; + let stream = connect_tls( + stream, + config.ssl_mode, + config.ssl_negotiation, + tls, + has_hostname, + ) + .await?; let mut stream = StartupStream { inner: Framed::new(stream, PostgresCodec), @@ -110,6 +117,7 @@ where let client = Client::new( sender, config.ssl_mode, + config.ssl_negotiation, process_id, secret_key, config.pgbouncer_mode, diff --git a/tokio-postgres/src/connect_tls.rs b/tokio-postgres/src/connect_tls.rs index 2b1229125..d220cd3b5 100644 --- a/tokio-postgres/src/connect_tls.rs +++ b/tokio-postgres/src/connect_tls.rs @@ -1,4 +1,4 @@ -use crate::config::SslMode; +use crate::config::{SslMode, SslNegotiation}; use crate::maybe_tls_stream::MaybeTlsStream; use crate::tls::private::ForcePrivateApi; use crate::tls::TlsConnect; @@ -10,6 +10,7 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; pub async fn connect_tls( mut stream: S, mode: SslMode, + negotiation: SslNegotiation, tls: T, has_hostname: bool, ) -> Result, Error> @@ -22,21 +23,26 @@ where SslMode::Prefer if !tls.can_connect(ForcePrivateApi) => { return Ok(MaybeTlsStream::Raw(stream)) } + SslMode::Prefer if negotiation == SslNegotiation::Direct => { + return Err(Error::tls("weak sslmode \"prefer\" may not be used with sslnegotiation=direct (use \"require\", \"verify-ca\", or \"verify-full\")".into())) + } SslMode::Prefer | SslMode::Require => {} } - let mut buf = BytesMut::new(); - frontend::ssl_request(&mut buf); - stream.write_all(&buf).await.map_err(Error::io)?; + if negotiation == SslNegotiation::Postgres { + let mut buf = BytesMut::new(); + frontend::ssl_request(&mut buf); + stream.write_all(&buf).await.map_err(Error::io)?; - let mut buf = [0]; - stream.read_exact(&mut buf).await.map_err(Error::io)?; + let mut buf = [0]; + stream.read_exact(&mut buf).await.map_err(Error::io)?; - if buf[0] != b'S' { - if SslMode::Require == mode { - return Err(Error::tls("server does not support TLS".into())); - } else { - return Ok(MaybeTlsStream::Raw(stream)); + if buf[0] != b'S' { + if SslMode::Require == mode { + return Err(Error::tls("server does not support TLS".into())); + } else { + return Ok(MaybeTlsStream::Raw(stream)); + } } } diff --git a/tokio-postgres/src/error/mod.rs b/tokio-postgres/src/error/mod.rs index 75664d258..07b3fd152 100644 --- a/tokio-postgres/src/error/mod.rs +++ b/tokio-postgres/src/error/mod.rs @@ -357,6 +357,7 @@ enum Kind { #[cfg(feature = "runtime")] Connect, Timeout, + UnsupportedType, } struct ErrorInner { @@ -399,6 +400,7 @@ impl fmt::Display for Error { #[cfg(feature = "runtime")] Kind::Connect => fmt.write_str("error connecting to server")?, Kind::Timeout => fmt.write_str("timeout waiting for server")?, + Kind::UnsupportedType => fmt.write_str("unsupported type")?, }; if let Some(ref cause) = self.0.cause { write!(fmt, ": {}", cause)?; @@ -450,6 +452,10 @@ impl Error { Error::new(Kind::UnexpectedMessage, None) } + pub(crate) fn unsupported_type() -> Error { + Error::new(Kind::UnsupportedType, None) + } + #[allow(clippy::needless_pass_by_value)] pub(crate) fn db(error: ErrorResponseBody) -> Error { match DbError::parse(&mut error.fields()) { diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index 6e7dffeb1..dcda147b5 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -80,7 +80,7 @@ pub trait GenericClient: private::Sealed { ) -> Result; /// Like [`Client::transaction`]. - async fn transaction(&mut self) -> Result, Error>; + async fn transaction<'a>(&'a mut self) -> Result, Error>; /// Like [`Client::batch_execute`]. async fn batch_execute(&self, query: &str) -> Result<(), Error>; @@ -180,7 +180,7 @@ impl GenericClient for Client { self.prepare_typed(query, parameter_types).await } - async fn transaction(&mut self) -> Result, Error> { + async fn transaction<'a>(&'a mut self) -> Result, Error> { self.transaction().await } diff --git a/tokio-postgres/src/prepare.rs b/tokio-postgres/src/prepare.rs index f2aeea007..91dda738a 100644 --- a/tokio-postgres/src/prepare.rs +++ b/tokio-postgres/src/prepare.rs @@ -17,18 +17,15 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; const TYPEINFO_QUERY: &str = "\ -SELECT t.typname, t.typtype, t.typelem, r.rngsubtype, t.typbasetype, n.nspname, t.typrelid +SELECT t.typname, t.typtype, t.typelem, t.typbasetype, t.typrelid FROM pg_catalog.pg_type t -LEFT OUTER JOIN pg_catalog.pg_range r ON r.rngtypid = t.oid -INNER JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = $1 "; // Range types weren't added until Postgres 9.2, so pg_range may not exist const TYPEINFO_FALLBACK_QUERY: &str = "\ -SELECT t.typname, t.typtype, t.typelem, NULL::OID, t.typbasetype, n.nspname, t.typrelid +SELECT t.typname, t.typtype, t.typelem, t.typbasetype, t.typrelid FROM pg_catalog.pg_type t -INNER JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = $1 "; @@ -153,10 +150,8 @@ pub(crate) async fn get_type(client: &Arc, oid: Oid) -> Result = row.try_get(3)?; - let basetype: Oid = row.try_get(4)?; - let schema: String = row.try_get(5)?; - let relid: Oid = row.try_get(6)?; + let basetype: Oid = row.try_get(3)?; + let relid: Oid = row.try_get(4)?; let kind = if type_ == b'e' as i8 { // Note: Quaint is not using the variants information at any time. @@ -173,14 +168,13 @@ pub(crate) async fn get_type(client: &Arc, oid: Oid) -> Result(&'a [T]); -impl<'a, T> fmt::Debug for BorrowToSqlParamsDebug<'a, T> +impl fmt::Debug for BorrowToSqlParamsDebug<'_, T> where T: BorrowToSql, { @@ -61,7 +61,7 @@ where }) } -pub async fn query_typed<'a, P, I>( +pub async fn query_typed( client: &Arc, query: &str, params: I, diff --git a/tokio-postgres/src/row.rs b/tokio-postgres/src/row.rs index 767c26921..ccb8817d0 100644 --- a/tokio-postgres/src/row.rs +++ b/tokio-postgres/src/row.rs @@ -79,9 +79,9 @@ impl RowIndex for str { } } -impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} +impl Sealed for &T where T: ?Sized + Sealed {} -impl<'a, T> RowIndex for &'a T +impl RowIndex for &T where T: ?Sized + RowIndex, { diff --git a/tokio-postgres/src/statement.rs b/tokio-postgres/src/statement.rs index bf9db30bb..1e9aba5fb 100644 --- a/tokio-postgres/src/statement.rs +++ b/tokio-postgres/src/statement.rs @@ -2,6 +2,7 @@ use crate::client::InnerClient; use crate::codec::FrontendMessage; use crate::connection::RequestMessages; use crate::types::Type; +use postgres_protocol::message::backend::Message; use postgres_protocol::message::frontend; use std::fmt; use std::sync::{Arc, Weak}; @@ -19,13 +20,28 @@ impl Drop for StatementInner { // Unnamed statements don't need to be closed return; } + if let Some(client) = self.client.upgrade() { let buf = client.with_buf(|buf| { frontend::close(b'S', &self.name, buf).unwrap(); frontend::sync(buf); buf.split().freeze() }); - let _ = client.send(RequestMessages::Single(FrontendMessage::Raw(buf))); + + let response = client.send(RequestMessages::Single(FrontendMessage::Raw(buf))); + + tokio::spawn(async move { + let result = async move { + match response?.next().await? { + Message::CloseComplete => Ok(()), + _ => Err(crate::Error::unexpected_message()), + } + }; + + if let Err(err) = result.await { + log::error!("failed to deallocate prepared statement: {err}"); + } + }); } } } diff --git a/tokio-postgres/src/to_statement.rs b/tokio-postgres/src/to_statement.rs index 427f77dd7..7e1299272 100644 --- a/tokio-postgres/src/to_statement.rs +++ b/tokio-postgres/src/to_statement.rs @@ -11,7 +11,7 @@ mod private { Query(&'a str), } - impl<'a> ToStatementType<'a> { + impl ToStatementType<'_> { pub async fn into_statement(self, client: &Client) -> Result { match self { ToStatementType::Statement(s) => Ok(s.clone()), diff --git a/tokio-postgres/src/transaction.rs b/tokio-postgres/src/transaction.rs index 17a50b60f..782c476c4 100644 --- a/tokio-postgres/src/transaction.rs +++ b/tokio-postgres/src/transaction.rs @@ -33,7 +33,7 @@ struct Savepoint { depth: u32, } -impl<'a> Drop for Transaction<'a> { +impl Drop for Transaction<'_> { fn drop(&mut self) { if self.done { return; diff --git a/tokio-postgres/src/transaction_builder.rs b/tokio-postgres/src/transaction_builder.rs index 93e9e9801..88c883176 100644 --- a/tokio-postgres/src/transaction_builder.rs +++ b/tokio-postgres/src/transaction_builder.rs @@ -113,7 +113,7 @@ impl<'a> TransactionBuilder<'a> { done: bool, } - impl<'a> Drop for RollbackIfNotDone<'a> { + impl Drop for RollbackIfNotDone<'_> { fn drop(&mut self) { if self.done { return; diff --git a/tokio-postgres/tests/test/parse.rs b/tokio-postgres/tests/test/parse.rs index 04d422e27..35eeca72b 100644 --- a/tokio-postgres/tests/test/parse.rs +++ b/tokio-postgres/tests/test/parse.rs @@ -1,5 +1,5 @@ use std::time::Duration; -use tokio_postgres::config::{Config, TargetSessionAttrs}; +use tokio_postgres::config::{Config, SslNegotiation, TargetSessionAttrs}; fn check(s: &str, config: &Config) { assert_eq!(s.parse::().expect(s), *config, "`{}`", s); @@ -42,6 +42,10 @@ fn settings() { .keepalives_idle(Duration::from_secs(30)) .target_session_attrs(TargetSessionAttrs::ReadOnly), ); + check( + "sslnegotiation=direct", + Config::new().ssl_negotiation(SslNegotiation::Direct), + ); } #[test] diff --git a/tokio-postgres/tests/test/types/mod.rs b/tokio-postgres/tests/test/types/mod.rs index 9b3aa4c81..6e83407ea 100644 --- a/tokio-postgres/tests/test/types/mod.rs +++ b/tokio-postgres/tests/test/types/mod.rs @@ -509,7 +509,7 @@ async fn domain() { to_sql_checked!(); } - impl<'a> FromSql<'a> for SessionId { + impl FromSql<'_> for SessionId { fn from_sql(ty: &Type, raw: &[u8]) -> result::Result> { Vec::::from_sql(ty, raw).map(SessionId) } diff --git a/tokio-postgres/tests/test/types/time_03.rs b/tokio-postgres/tests/test/types/time_03.rs index df013c9bf..fca940a97 100644 --- a/tokio-postgres/tests/test/types/time_03.rs +++ b/tokio-postgres/tests/test/types/time_03.rs @@ -1,5 +1,11 @@ +use std::fmt; + +use postgres_types::FromSqlOwned; use time_03::{format_description, OffsetDateTime, PrimitiveDateTime}; -use tokio_postgres::types::{Date, Timestamp}; +use tokio_postgres::{ + types::{Date, Timestamp}, + Client, +}; use crate::types::test_type; @@ -147,3 +153,33 @@ async fn test_time_params() { ) .await; } + +#[tokio::test] +async fn test_special_params_without_wrapper() { + async fn assert_overflows(client: &mut Client, val: &str, sql_type: &str) + where + T: FromSqlOwned + fmt::Debug, + { + let err = client + .query_one(&*format!("SELECT {}::{}", val, sql_type), &[]) + .await + .unwrap() + .try_get::<_, T>(0) + .unwrap_err(); + assert_eq!( + err.to_string(), + "error deserializing column 0: value too large to decode" + ); + } + + let mut client = crate::connect("user=postgres").await; + + assert_overflows::(&mut client, "'-infinity'", "timestamptz").await; + assert_overflows::(&mut client, "'infinity'", "timestamptz").await; + + assert_overflows::(&mut client, "'-infinity'", "timestamp").await; + assert_overflows::(&mut client, "'infinity'", "timestamp").await; + + assert_overflows::(&mut client, "'-infinity'", "date").await; + assert_overflows::(&mut client, "'infinity'", "date").await; +}