From b46ea89c42441187d15cd70e651907e29d6338c8 Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Fri, 26 Aug 2022 13:46:04 -0700 Subject: [PATCH 001/207] Add hostaddr support --- tokio-postgres/src/config.rs | 70 +++++++++++++++++++++++++++++++++++ tokio-postgres/src/connect.rs | 23 +++++++++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 2c29d629c..f29eed2b1 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -12,6 +12,7 @@ use crate::{Client, Connection, Error}; use std::borrow::Cow; #[cfg(unix)] use std::ffi::OsStr; +use std::ops::Deref; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; #[cfg(unix)] @@ -90,6 +91,17 @@ 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. +/// * `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, +/// - or if host specifies an IP address, that value will be used directly. +/// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications +/// with time constraints. However, a host name is required for verify-full SSL certificate verification. +/// Note that `host` is always required regardless of whether `hostaddr` is present. +/// * If `host` is specified without `hostaddr`, a host name lookup occurs; +/// * If both `host` and `hostaddr` are specified, the value for `hostaddr` gives the server network address. +/// The value for `host` is ignored unless the authentication method requires it, +/// in which case it will be used as the host name. /// * `port` - The port to connect to. Multiple ports can be specified, separated by commas. The number of ports must be /// either 1, in which case it will be used for all hosts, or the same as the number of hosts. Defaults to 5432 if /// omitted or the empty string. @@ -117,6 +129,10 @@ pub enum Host { /// ``` /// /// ```not_rust +/// host=host1,host2,host3 port=1234,,5678 hostaddr=127.0.0.1,127.0.0.2,127.0.0.3 user=postgres target_session_attrs=read-write +/// ``` +/// +/// ```not_rust /// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write /// ``` /// @@ -153,6 +169,7 @@ pub struct Config { pub(crate) application_name: Option, pub(crate) ssl_mode: SslMode, pub(crate) host: Vec, + pub(crate) hostaddr: Vec, pub(crate) port: Vec, pub(crate) connect_timeout: Option, pub(crate) keepalives: bool, @@ -178,6 +195,7 @@ impl Config { application_name: None, ssl_mode: SslMode::Prefer, host: vec![], + hostaddr: vec![], port: vec![], connect_timeout: None, keepalives: true, @@ -288,6 +306,11 @@ impl Config { &self.host } + /// Gets the hostaddrs that have been added to the configuration with `hostaddr`. + pub fn get_hostaddrs(&self) -> &[String] { + self.hostaddr.deref() + } + /// Adds a Unix socket host to the configuration. /// /// Unlike `host`, this method allows non-UTF8 paths. @@ -300,6 +323,15 @@ impl Config { self } + /// Adds a hostaddr to the configuration. + /// + /// Multiple hostaddrs can be specified by calling this method multiple times, and each will be tried in order. + /// There must be either no hostaddrs, or the same number of hostaddrs as hosts. + pub fn hostaddr(&mut self, hostaddr: &str) -> &mut Config { + self.hostaddr.push(hostaddr.to_string()); + self + } + /// Adds a port to the configuration. /// /// Multiple ports can be specified by calling this method multiple times. There must either be no ports, in which @@ -418,6 +450,11 @@ impl Config { self.host(host); } } + "hostaddr" => { + for hostaddr in value.split(',') { + self.hostaddr(hostaddr); + } + } "port" => { for port in value.split(',') { let port = if port.is_empty() { @@ -542,6 +579,7 @@ impl fmt::Debug for Config { .field("application_name", &self.application_name) .field("ssl_mode", &self.ssl_mode) .field("host", &self.host) + .field("hostaddr", &self.hostaddr) .field("port", &self.port) .field("connect_timeout", &self.connect_timeout) .field("keepalives", &self.keepalives) @@ -922,3 +960,35 @@ impl<'a> UrlParser<'a> { .map_err(|e| Error::config_parse(e.into())) } } + +#[cfg(test)] +mod tests { + use crate::{config::Host, Config}; + + #[test] + fn test_simple_parsing() { + let s = "user=pass_user dbname=postgres host=host1,host2 hostaddr=127.0.0.1,127.0.0.2 port=26257"; + let config = s.parse::().unwrap(); + assert_eq!(Some("pass_user"), config.get_user()); + assert_eq!(Some("postgres"), config.get_dbname()); + assert_eq!( + [ + Host::Tcp("host1".to_string()), + Host::Tcp("host2".to_string()) + ], + config.get_hosts(), + ); + + assert_eq!(["127.0.0.1", "127.0.0.2"], config.get_hostaddrs(),); + + assert_eq!(1, 1); + } + + #[test] + fn test_empty_hostaddrs() { + let s = + "user=pass_user dbname=postgres host=host1,host2,host3 hostaddr=127.0.0.1,,127.0.0.2"; + let config = s.parse::().unwrap(); + assert_eq!(["127.0.0.1", "", "127.0.0.2"], config.get_hostaddrs(),); + } +} diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 88faafe6b..e8ac29b42 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -23,6 +23,15 @@ where return Err(Error::config("invalid number of ports".into())); } + if !config.hostaddr.is_empty() && config.hostaddr.len() != config.host.len() { + let msg = format!( + "invalid number of hostaddrs ({}). Possible values: 0 or number of hosts ({})", + config.hostaddr.len(), + config.host.len(), + ); + return Err(Error::config(msg.into())); + } + let mut error = None; for (i, host) in config.host.iter().enumerate() { let port = config @@ -32,18 +41,28 @@ where .copied() .unwrap_or(5432); + // The value of host is always used as the hostname for TLS validation. let hostname = match host { Host::Tcp(host) => host.as_str(), // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter #[cfg(unix)] Host::Unix(_) => "", }; - let tls = tls .make_tls_connect(hostname) .map_err(|e| Error::tls(e.into()))?; - match connect_once(host, port, tls, config).await { + // If both host and hostaddr are specified, the value of hostaddr is used to to establish the TCP connection. + let hostaddr = match host { + Host::Tcp(_hostname) => match config.hostaddr.get(i) { + Some(hostaddr) if hostaddr.is_empty() => Host::Tcp(hostaddr.clone()), + _ => host.clone(), + }, + #[cfg(unix)] + Host::Unix(_v) => host.clone(), + }; + + match connect_once(&hostaddr, port, tls, config).await { Ok((client, connection)) => return Ok((client, connection)), Err(e) => error = Some(e), } From 3c9315e3200f5eb99bb5a9b5998aca555951d691 Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sat, 27 Aug 2022 11:40:57 -0700 Subject: [PATCH 002/207] IpAddr + try hostaddr first --- tokio-postgres/src/config.rs | 36 ++++++++++-------- tokio-postgres/src/connect.rs | 61 +++++++++++++++++++------------ tokio-postgres/tests/test/main.rs | 52 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 39 deletions(-) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 0c62b5030..34accdbe8 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -13,6 +13,7 @@ use crate::{Client, Connection, Error}; use std::borrow::Cow; #[cfg(unix)] use std::ffi::OsStr; +use std::net::IpAddr; use std::ops::Deref; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; @@ -98,7 +99,9 @@ pub enum Host { /// - or if host specifies an IP address, that value will be used directly. /// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications /// with time constraints. However, a host name is required for verify-full SSL certificate verification. -/// Note that `host` is always required regardless of whether `hostaddr` is present. +/// Specifically: +/// * If `hostaddr` is specified without `host`, the value for `hostaddr` gives the server network address. +/// The connection attempt will fail if the authentication method requires a host name; /// * If `host` is specified without `hostaddr`, a host name lookup occurs; /// * If both `host` and `hostaddr` are specified, the value for `hostaddr` gives the server network address. /// The value for `host` is ignored unless the authentication method requires it, @@ -174,7 +177,7 @@ pub struct Config { pub(crate) application_name: Option, pub(crate) ssl_mode: SslMode, pub(crate) host: Vec, - pub(crate) hostaddr: Vec, + pub(crate) hostaddr: Vec, pub(crate) port: Vec, pub(crate) connect_timeout: Option, pub(crate) keepalives: bool, @@ -317,7 +320,7 @@ impl Config { } /// Gets the hostaddrs that have been added to the configuration with `hostaddr`. - pub fn get_hostaddrs(&self) -> &[String] { + pub fn get_hostaddrs(&self) -> &[IpAddr] { self.hostaddr.deref() } @@ -337,8 +340,8 @@ impl Config { /// /// Multiple hostaddrs can be specified by calling this method multiple times, and each will be tried in order. /// There must be either no hostaddrs, or the same number of hostaddrs as hosts. - pub fn hostaddr(&mut self, hostaddr: &str) -> &mut Config { - self.hostaddr.push(hostaddr.to_string()); + pub fn hostaddr(&mut self, hostaddr: IpAddr) -> &mut Config { + self.hostaddr.push(hostaddr); self } @@ -489,7 +492,10 @@ impl Config { } "hostaddr" => { for hostaddr in value.split(',') { - self.hostaddr(hostaddr); + let addr = hostaddr + .parse() + .map_err(|_| Error::config_parse(Box::new(InvalidValue("hostaddr"))))?; + self.hostaddr(addr); } } "port" => { @@ -1016,6 +1022,8 @@ impl<'a> UrlParser<'a> { #[cfg(test)] mod tests { + use std::net::IpAddr; + use crate::{config::Host, Config}; #[test] @@ -1032,16 +1040,14 @@ mod tests { config.get_hosts(), ); - assert_eq!(["127.0.0.1", "127.0.0.2"], config.get_hostaddrs(),); + assert_eq!( + [ + "127.0.0.1".parse::().unwrap(), + "127.0.0.2".parse::().unwrap() + ], + config.get_hostaddrs(), + ); assert_eq!(1, 1); } - - #[test] - fn test_empty_hostaddrs() { - let s = - "user=pass_user dbname=postgres host=host1,host2,host3 hostaddr=127.0.0.1,,127.0.0.2"; - let config = s.parse::().unwrap(); - assert_eq!(["127.0.0.1", "", "127.0.0.2"], config.get_hostaddrs(),); - } } diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index c36677234..ee1dc1c76 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -5,8 +5,8 @@ use crate::connect_socket::connect_socket; use crate::tls::{MakeTlsConnect, TlsConnect}; use crate::{Client, Config, Connection, Error, SimpleQueryMessage, Socket}; use futures_util::{future, pin_mut, Future, FutureExt, Stream}; -use std::io; use std::task::Poll; +use std::{cmp, io}; pub async fn connect( mut tls: T, @@ -15,25 +15,35 @@ pub async fn connect( where T: MakeTlsConnect, { - if config.host.is_empty() { - return Err(Error::config("host missing".into())); + if config.host.is_empty() && config.hostaddr.is_empty() { + return Err(Error::config("both host and hostaddr are missing".into())); } - if config.port.len() > 1 && config.port.len() != config.host.len() { - return Err(Error::config("invalid number of ports".into())); - } - - if !config.hostaddr.is_empty() && config.hostaddr.len() != config.host.len() { + if !config.host.is_empty() + && !config.hostaddr.is_empty() + && config.host.len() != config.hostaddr.len() + { let msg = format!( - "invalid number of hostaddrs ({}). Possible values: 0 or number of hosts ({})", - config.hostaddr.len(), + "number of hosts ({}) is different from number of hostaddrs ({})", config.host.len(), + config.hostaddr.len(), ); return Err(Error::config(msg.into())); } + // At this point, either one of the following two scenarios could happen: + // (1) either config.host or config.hostaddr must be empty; + // (2) if both config.host and config.hostaddr are NOT empty; their lengths must be equal. + let num_hosts = cmp::max(config.host.len(), config.hostaddr.len()); + + if config.port.len() > 1 && config.port.len() != num_hosts { + return Err(Error::config("invalid number of ports".into())); + } + let mut error = None; - for (i, host) in config.host.iter().enumerate() { + for i in 0..num_hosts { + let host = config.host.get(i); + let hostaddr = config.hostaddr.get(i); let port = config .port .get(i) @@ -42,27 +52,30 @@ where .unwrap_or(5432); // The value of host is always used as the hostname for TLS validation. + // postgres doesn't support TLS over unix sockets, so the choice for Host::Unix variant here doesn't matter let hostname = match host { - Host::Tcp(host) => host.as_str(), - // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter - #[cfg(unix)] - Host::Unix(_) => "", + Some(Host::Tcp(host)) => host.as_str(), + _ => "", }; let tls = tls .make_tls_connect(hostname) .map_err(|e| Error::tls(e.into()))?; - // If both host and hostaddr are specified, the value of hostaddr is used to to establish the TCP connection. - let hostaddr = match host { - Host::Tcp(_hostname) => match config.hostaddr.get(i) { - Some(hostaddr) if hostaddr.is_empty() => Host::Tcp(hostaddr.clone()), - _ => host.clone(), - }, - #[cfg(unix)] - Host::Unix(_v) => host.clone(), + // Try to use the value of hostaddr to establish the TCP connection, + // fallback to host if hostaddr is not present. + let addr = match hostaddr { + Some(ipaddr) => Host::Tcp(ipaddr.to_string()), + None => { + if let Some(host) = host { + host.clone() + } else { + // This is unreachable. + return Err(Error::config("both host and hostaddr are empty".into())); + } + } }; - match connect_once(&hostaddr, port, tls, config).await { + match connect_once(&addr, port, tls, config).await { Ok((client, connection)) => return Ok((client, connection)), Err(e) => error = Some(e), } diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 0ab4a7bab..387c90d7c 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -147,6 +147,58 @@ async fn scram_password_ok() { connect("user=scram_user password=password dbname=postgres").await; } +#[tokio::test] +async fn host_only_ok() { + let _ = tokio_postgres::connect( + "host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_only_ok() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1 port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_and_host_ok() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1 host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_host_mismatch() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1,127.0.0.2 host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .err() + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_host_both_missing() { + let _ = tokio_postgres::connect( + "port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .err() + .unwrap(); +} + #[tokio::test] async fn pipelined_prepare() { let client = connect("user=postgres").await; From e30bff65a35d1240f8b920c49569a40563712e5d Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sat, 27 Aug 2022 11:55:11 -0700 Subject: [PATCH 003/207] also update postgres --- postgres/src/config.rs | 33 +++++++++++++++++++++++++++++++++ tokio-postgres/src/config.rs | 1 + 2 files changed, 34 insertions(+) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index b541ec846..a754ff91f 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -6,6 +6,7 @@ use crate::connection::Connection; use crate::Client; use log::info; use std::fmt; +use std::net::IpAddr; use std::path::Path; use std::str::FromStr; use std::sync::Arc; @@ -39,6 +40,19 @@ 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. +/// * `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, +/// - or if host specifies an IP address, that value will be used directly. +/// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications +/// with time constraints. However, a host name is required for verify-full SSL certificate verification. +/// Specifically: +/// * If `hostaddr` is specified without `host`, the value for `hostaddr` gives the server network address. +/// The connection attempt will fail if the authentication method requires a host name; +/// * If `host` is specified without `hostaddr`, a host name lookup occurs; +/// * If both `host` and `hostaddr` are specified, the value for `hostaddr` gives the server network address. +/// The value for `host` is ignored unless the authentication method requires it, +/// in which case it will be used as the host name. /// * `port` - The port to connect to. Multiple ports can be specified, separated by commas. The number of ports must be /// either 1, in which case it will be used for all hosts, or the same as the number of hosts. Defaults to 5432 if /// omitted or the empty string. @@ -67,6 +81,10 @@ use tokio_postgres::{Error, Socket}; /// ``` /// /// ```not_rust +/// host=host1,host2,host3 port=1234,,5678 hostaddr=127.0.0.1,127.0.0.2,127.0.0.3 user=postgres target_session_attrs=read-write +/// ``` +/// +/// ```not_rust /// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write /// ``` /// @@ -204,6 +222,7 @@ impl Config { /// /// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix /// systems, a host starting with a `/` is interpreted as a path to a directory containing Unix domain sockets. + /// There must be either no hosts, or the same number of hosts as hostaddrs. pub fn host(&mut self, host: &str) -> &mut Config { self.config.host(host); self @@ -214,6 +233,11 @@ impl Config { self.config.get_hosts() } + /// Gets the hostaddrs that have been added to the configuration with `hostaddr`. + pub fn get_hostaddrs(&self) -> &[IpAddr] { + self.config.get_hostaddrs() + } + /// Adds a Unix socket host to the configuration. /// /// Unlike `host`, this method allows non-UTF8 paths. @@ -226,6 +250,15 @@ impl Config { self } + /// Adds a hostaddr to the configuration. + /// + /// Multiple hostaddrs can be specified by calling this method multiple times, and each will be tried in order. + /// There must be either no hostaddrs, or the same number of hostaddrs as hosts. + pub fn hostaddr(&mut self, hostaddr: IpAddr) -> &mut Config { + self.config.hostaddr(hostaddr); + self + } + /// Adds a port to the configuration. /// /// Multiple ports can be specified by calling this method multiple times. There must either be no ports, in which diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 34accdbe8..923da2985 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -302,6 +302,7 @@ impl Config { /// /// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix /// systems, a host starting with a `/` is interpreted as a path to a directory containing Unix domain sockets. + /// There must be either no hosts, or the same number of hosts as hostaddrs. pub fn host(&mut self, host: &str) -> &mut Config { #[cfg(unix)] { From 6c49a452feb273430d0091de83961ad65ffb9102 Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sat, 27 Aug 2022 11:55:47 -0700 Subject: [PATCH 004/207] fmt --- postgres/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index a754ff91f..921566b66 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -83,7 +83,7 @@ use tokio_postgres::{Error, Socket}; /// ```not_rust /// host=host1,host2,host3 port=1234,,5678 hostaddr=127.0.0.1,127.0.0.2,127.0.0.3 user=postgres target_session_attrs=read-write /// ``` -/// +/// /// ```not_rust /// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write /// ``` @@ -236,7 +236,7 @@ impl Config { /// Gets the hostaddrs that have been added to the configuration with `hostaddr`. pub fn get_hostaddrs(&self) -> &[IpAddr] { self.config.get_hostaddrs() - } + } /// Adds a Unix socket host to the configuration. /// From 42fef24973dff5450b294df21e94e665fe4d996d Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sun, 28 Aug 2022 12:09:53 -0700 Subject: [PATCH 005/207] explicitly handle host being None --- tokio-postgres/src/connect.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index ee1dc1c76..63574516c 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -51,14 +51,17 @@ where .copied() .unwrap_or(5432); - // The value of host is always used as the hostname for TLS validation. - // postgres doesn't support TLS over unix sockets, so the choice for Host::Unix variant here doesn't matter + // The value of host is used as the hostname for TLS validation, + // if it's not present, use the value of hostaddr. let hostname = match host { - Some(Host::Tcp(host)) => host.as_str(), - _ => "", + Some(Host::Tcp(host)) => host.clone(), + // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter Some() + #[cfg(unix)] + Some(Host::Unix(_)) => "".to_string(), + None => hostaddr.map_or("".to_string(), |ipaddr| ipaddr.to_string()), }; let tls = tls - .make_tls_connect(hostname) + .make_tls_connect(&hostname) .map_err(|e| Error::tls(e.into()))?; // Try to use the value of hostaddr to establish the TCP connection, From 9b34d74df143527602a18b1564b554647dbf5eaf Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sun, 28 Aug 2022 12:18:36 -0700 Subject: [PATCH 006/207] add negative test --- tokio-postgres/src/config.rs | 6 ++++++ tokio-postgres/src/connect.rs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 923da2985..e5bed8ddf 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -1051,4 +1051,10 @@ mod tests { assert_eq!(1, 1); } + + #[test] + fn test_invalid_hostaddr_parsing() { + let s = "user=pass_user dbname=postgres host=host1 hostaddr=127.0.0 port=26257"; + s.parse::().err().unwrap(); + } } diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 63574516c..888f9cf8a 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -55,7 +55,7 @@ where // if it's not present, use the value of hostaddr. let hostname = match host { Some(Host::Tcp(host)) => host.clone(), - // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter Some() + // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter #[cfg(unix)] Some(Host::Unix(_)) => "".to_string(), None => hostaddr.map_or("".to_string(), |ipaddr| ipaddr.to_string()), From 8ac10ff1de52281592d5bdd75e109d995ca33a2c Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Tue, 30 Aug 2022 22:10:19 -0700 Subject: [PATCH 007/207] move test to runtime --- tokio-postgres/tests/test/main.rs | 52 ---------------------------- tokio-postgres/tests/test/runtime.rs | 52 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 387c90d7c..0ab4a7bab 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -147,58 +147,6 @@ async fn scram_password_ok() { connect("user=scram_user password=password dbname=postgres").await; } -#[tokio::test] -async fn host_only_ok() { - let _ = tokio_postgres::connect( - "host=localhost port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn hostaddr_only_ok() { - let _ = tokio_postgres::connect( - "hostaddr=127.0.0.1 port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn hostaddr_and_host_ok() { - let _ = tokio_postgres::connect( - "hostaddr=127.0.0.1 host=localhost port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn hostaddr_host_mismatch() { - let _ = tokio_postgres::connect( - "hostaddr=127.0.0.1,127.0.0.2 host=localhost port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .err() - .unwrap(); -} - -#[tokio::test] -async fn hostaddr_host_both_missing() { - let _ = tokio_postgres::connect( - "port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .err() - .unwrap(); -} - #[tokio::test] async fn pipelined_prepare() { let client = connect("user=postgres").await; diff --git a/tokio-postgres/tests/test/runtime.rs b/tokio-postgres/tests/test/runtime.rs index 67b4ead8a..86c1f0701 100644 --- a/tokio-postgres/tests/test/runtime.rs +++ b/tokio-postgres/tests/test/runtime.rs @@ -66,6 +66,58 @@ async fn target_session_attrs_err() { .unwrap(); } +#[tokio::test] +async fn host_only_ok() { + let _ = tokio_postgres::connect( + "host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_only_ok() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1 port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_and_host_ok() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1 host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_host_mismatch() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1,127.0.0.2 host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .err() + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_host_both_missing() { + let _ = tokio_postgres::connect( + "port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .err() + .unwrap(); +} + #[tokio::test] async fn cancel_query() { let client = connect("host=localhost port=5433 user=postgres").await; From 7faeea5acc3c24493b97306f0238712bf7e0b4e9 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 21 Nov 2022 15:19:11 -0800 Subject: [PATCH 008/207] Fix clippy/deprecation warnings --- postgres-protocol/src/authentication/mod.rs | 2 +- postgres-types/src/chrono_04.rs | 8 ++++---- postgres-types/src/geo_types_07.rs | 4 ++-- postgres-types/src/lib.rs | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/postgres-protocol/src/authentication/mod.rs b/postgres-protocol/src/authentication/mod.rs index 9cfd6034c..71afa4b9b 100644 --- a/postgres-protocol/src/authentication/mod.rs +++ b/postgres-protocol/src/authentication/mod.rs @@ -15,7 +15,7 @@ pub fn md5_hash(username: &[u8], password: &[u8], salt: [u8; 4]) -> String { md5.update(username); let output = md5.finalize_reset(); md5.update(format!("{:x}", output)); - md5.update(&salt); + md5.update(salt); format!("md5{:x}", md5.finalize()) } diff --git a/postgres-types/src/chrono_04.rs b/postgres-types/src/chrono_04.rs index fcd25e6d1..180ee3766 100644 --- a/postgres-types/src/chrono_04.rs +++ b/postgres-types/src/chrono_04.rs @@ -6,7 +6,7 @@ use std::error::Error; use crate::{FromSql, IsNull, ToSql, Type}; fn base() -> NaiveDateTime { - NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0) + NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap() } impl<'a> FromSql<'a> for NaiveDateTime { @@ -84,7 +84,7 @@ impl<'a> FromSql<'a> for DateTime { raw: &[u8], ) -> Result, Box> { let utc = DateTime::::from_sql(type_, raw)?; - Ok(utc.with_timezone(&FixedOffset::east(0))) + Ok(utc.with_timezone(&FixedOffset::east_opt(0).unwrap())) } accepts!(TIMESTAMPTZ); @@ -133,7 +133,7 @@ impl ToSql for NaiveDate { impl<'a> FromSql<'a> for NaiveTime { fn from_sql(_: &Type, raw: &[u8]) -> Result> { let usec = types::time_from_sql(raw)?; - Ok(NaiveTime::from_hms(0, 0, 0) + Duration::microseconds(usec)) + Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap() + Duration::microseconds(usec)) } accepts!(TIME); @@ -141,7 +141,7 @@ impl<'a> FromSql<'a> for NaiveTime { impl ToSql for NaiveTime { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - let delta = self.signed_duration_since(NaiveTime::from_hms(0, 0, 0)); + let delta = self.signed_duration_since(NaiveTime::from_hms_opt(0, 0, 0).unwrap()); let time = match delta.num_microseconds() { Some(time) => time, None => return Err("value too large to transmit".into()), diff --git a/postgres-types/src/geo_types_07.rs b/postgres-types/src/geo_types_07.rs index 7dfb51056..bf7fa5601 100644 --- a/postgres-types/src/geo_types_07.rs +++ b/postgres-types/src/geo_types_07.rs @@ -1,6 +1,6 @@ use bytes::BytesMut; use fallible_iterator::FallibleIterator; -use geo_types_0_7::{Coordinate, LineString, Point, Rect}; +use geo_types_0_7::{Coord, LineString, Point, Rect}; use postgres_protocol::types; use std::error::Error; @@ -52,7 +52,7 @@ impl<'a> FromSql<'a> for LineString { let path = types::path_from_sql(raw)?; let points = path .points() - .map(|p| Ok(Coordinate { x: p.x(), y: p.y() })) + .map(|p| Ok(Coord { x: p.x(), y: p.y() })) .collect()?; Ok(LineString(points)) } diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index f5d841cd1..ca4233f8a 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -938,7 +938,7 @@ impl<'a, T: ToSql> ToSql for &'a [T] { impl<'a> ToSql for &'a [u8] { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - types::bytea_to_sql(*self, w); + types::bytea_to_sql(self, w); Ok(IsNull::No) } @@ -1011,10 +1011,10 @@ impl ToSql for Vec { impl<'a> ToSql for &'a str { fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { match *ty { - ref ty if ty.name() == "ltree" => types::ltree_to_sql(*self, w), - ref ty if ty.name() == "lquery" => types::lquery_to_sql(*self, w), - ref ty if ty.name() == "ltxtquery" => types::ltxtquery_to_sql(*self, w), - _ => types::text_to_sql(*self, w), + ref ty if ty.name() == "ltree" => types::ltree_to_sql(self, w), + ref ty if ty.name() == "lquery" => types::lquery_to_sql(self, w), + ref ty if ty.name() == "ltxtquery" => types::ltxtquery_to_sql(self, w), + _ => types::text_to_sql(self, w), } Ok(IsNull::No) } From 6c18ac09b95a65af45be899f9151928d91ecb48a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 21 Nov 2022 15:20:25 -0800 Subject: [PATCH 009/207] rustfmt --- postgres-types/src/chrono_04.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/postgres-types/src/chrono_04.rs b/postgres-types/src/chrono_04.rs index 180ee3766..0ec92437d 100644 --- a/postgres-types/src/chrono_04.rs +++ b/postgres-types/src/chrono_04.rs @@ -6,7 +6,10 @@ use std::error::Error; use crate::{FromSql, IsNull, ToSql, Type}; fn base() -> NaiveDateTime { - NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap() + NaiveDate::from_ymd_opt(2000, 1, 1) + .unwrap() + .and_hms_opt(0, 0, 0) + .unwrap() } impl<'a> FromSql<'a> for NaiveDateTime { From 6557bca87137207adc7aa4ca3c3eaee4f3beffb6 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 21 Nov 2022 15:25:39 -0800 Subject: [PATCH 010/207] more clippy --- postgres-derive-test/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/postgres-derive-test/src/lib.rs b/postgres-derive-test/src/lib.rs index 8bfd147fb..d1478ac4c 100644 --- a/postgres-derive-test/src/lib.rs +++ b/postgres-derive-test/src/lib.rs @@ -16,12 +16,12 @@ where { for &(ref val, ref repr) in checks.iter() { let stmt = conn - .prepare(&*format!("SELECT {}::{}", *repr, sql_type)) + .prepare(&format!("SELECT {}::{}", *repr, sql_type)) .unwrap(); let result = conn.query_one(&stmt, &[]).unwrap().get(0); assert_eq!(val, &result); - let stmt = conn.prepare(&*format!("SELECT $1::{}", sql_type)).unwrap(); + let stmt = conn.prepare(&format!("SELECT $1::{}", sql_type)).unwrap(); let result = conn.query_one(&stmt, &[val]).unwrap().get(0); assert_eq!(val, &result); } @@ -40,12 +40,12 @@ pub fn test_type_asymmetric( { for &(ref val, ref repr) in checks.iter() { let stmt = conn - .prepare(&*format!("SELECT {}::{}", *repr, sql_type)) + .prepare(&format!("SELECT {}::{}", *repr, sql_type)) .unwrap(); let result: F = conn.query_one(&stmt, &[]).unwrap().get(0); assert!(cmp(val, &result)); - let stmt = conn.prepare(&*format!("SELECT $1::{}", sql_type)).unwrap(); + let stmt = conn.prepare(&format!("SELECT $1::{}", sql_type)).unwrap(); let result: F = conn.query_one(&stmt, &[val]).unwrap().get(0); assert!(cmp(val, &result)); } From f48c3b577f3e4515f2d0e88dcf026295194e4295 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 21 Nov 2022 15:26:37 -0800 Subject: [PATCH 011/207] more deprecations --- tokio-postgres/tests/test/types/geo_types_07.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tokio-postgres/tests/test/types/geo_types_07.rs b/tokio-postgres/tests/test/types/geo_types_07.rs index 85ff2553a..43a13f451 100644 --- a/tokio-postgres/tests/test/types/geo_types_07.rs +++ b/tokio-postgres/tests/test/types/geo_types_07.rs @@ -1,5 +1,5 @@ #[cfg(feature = "with-geo-types-0_7")] -use geo_types_07::{Coordinate, LineString, Point, Rect}; +use geo_types_07::{Coord, LineString, Point, Rect}; use crate::types::test_type; @@ -23,8 +23,8 @@ async fn test_box_params() { &[ ( Some(Rect::new( - Coordinate { x: -3.2, y: 1.618 }, - Coordinate { + Coord { x: -3.2, y: 1.618 }, + Coord { x: 160.0, y: 69701.5615, }, @@ -40,9 +40,9 @@ async fn test_box_params() { #[tokio::test] async fn test_path_params() { let points = vec![ - Coordinate { x: 0., y: 0. }, - Coordinate { x: -3.2, y: 1.618 }, - Coordinate { + Coord { x: 0., y: 0. }, + Coord { x: -3.2, y: 1.618 }, + Coord { x: 160.0, y: 69701.5615, }, From b29164d53a7bcc16fed7aceb7cb6cb9a3faf214a Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Mon, 21 Nov 2022 12:15:38 -0500 Subject: [PATCH 012/207] Add idle session timeout error This commit adds the idle session timeout error (57P05). See https://www.postgresql.org/docs/current/errcodes-appendix.html --- tokio-postgres/src/error/sqlstate.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tokio-postgres/src/error/sqlstate.rs b/tokio-postgres/src/error/sqlstate.rs index 71648a948..4eb1ae2d0 100644 --- a/tokio-postgres/src/error/sqlstate.rs +++ b/tokio-postgres/src/error/sqlstate.rs @@ -232,6 +232,7 @@ impl SqlState { Inner::E57P02 => "57P02", Inner::E57P03 => "57P03", Inner::E57P04 => "57P04", + Inner::E57P05 => "57P05", Inner::E58000 => "58000", Inner::E58030 => "58030", Inner::E58P01 => "58P01", @@ -946,6 +947,9 @@ impl SqlState { /// 57P04 pub const DATABASE_DROPPED: SqlState = SqlState(Inner::E57P04); + /// 57P05 + pub const IDLE_SESSION_TIMEOUT: SqlState = SqlState(Inner::E57P05); + /// 58000 pub const SYSTEM_ERROR: SqlState = SqlState(Inner::E58000); @@ -1292,6 +1296,7 @@ enum Inner { E57P02, E57P03, E57P04, + E57P05, E58000, E58030, E58P01, @@ -1498,6 +1503,7 @@ static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ("55006", SqlState::OBJECT_IN_USE), ("42P01", SqlState::UNDEFINED_TABLE), ("25P03", SqlState::IDLE_IN_TRANSACTION_SESSION_TIMEOUT), + ("57P05", SqlState::IDLE_SESSION_TIMEOUT), ("22037", SqlState::NON_UNIQUE_KEYS_IN_A_JSON_OBJECT), ("2203A", SqlState::SQL_JSON_MEMBER_NOT_FOUND), ("P0004", SqlState::ASSERT_FAILURE), From 664345f9ec06920c3129be2f1b7e0dd6c031c8ef Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Mon, 21 Nov 2022 14:50:52 -0500 Subject: [PATCH 013/207] Update PostgreSQL files to most recent version --- codegen/src/errcodes.txt | 4 +- codegen/src/pg_range.dat | 23 +++--- codegen/src/pg_type.dat | 160 +++++++++++++++++++++++++++------------ 3 files changed, 127 insertions(+), 60 deletions(-) diff --git a/codegen/src/errcodes.txt b/codegen/src/errcodes.txt index c79312ed0..62418a051 100644 --- a/codegen/src/errcodes.txt +++ b/codegen/src/errcodes.txt @@ -2,7 +2,7 @@ # errcodes.txt # PostgreSQL error codes # -# Copyright (c) 2003-2020, PostgreSQL Global Development Group +# Copyright (c) 2003-2022, PostgreSQL Global Development Group # # This list serves as the basis for generating source files containing error # codes. It is kept in a common format to make sure all these source files have @@ -222,6 +222,7 @@ Section: Class 22 - Data Exception 2203D E ERRCODE_TOO_MANY_JSON_ARRAY_ELEMENTS too_many_json_array_elements 2203E E ERRCODE_TOO_MANY_JSON_OBJECT_MEMBERS too_many_json_object_members 2203F E ERRCODE_SQL_JSON_SCALAR_REQUIRED sql_json_scalar_required +2203G E ERRCODE_SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE sql_json_item_cannot_be_cast_to_target_type Section: Class 23 - Integrity Constraint Violation @@ -428,6 +429,7 @@ Section: Class 57 - Operator Intervention 57P02 E ERRCODE_CRASH_SHUTDOWN crash_shutdown 57P03 E ERRCODE_CANNOT_CONNECT_NOW cannot_connect_now 57P04 E ERRCODE_DATABASE_DROPPED database_dropped +57P05 E ERRCODE_IDLE_SESSION_TIMEOUT idle_session_timeout Section: Class 58 - System Error (errors external to PostgreSQL itself) diff --git a/codegen/src/pg_range.dat b/codegen/src/pg_range.dat index 479754c24..74d6de0cf 100644 --- a/codegen/src/pg_range.dat +++ b/codegen/src/pg_range.dat @@ -3,7 +3,7 @@ # pg_range.dat # Initial contents of the pg_range system catalog. # -# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group +# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # # src/include/catalog/pg_range.dat @@ -12,20 +12,23 @@ [ -{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops', +{ rngtypid => 'int4range', rngsubtype => 'int4', + rngmultitypid => 'int4multirange', rngsubopc => 'btree/int4_ops', rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' }, { rngtypid => 'numrange', rngsubtype => 'numeric', - rngsubopc => 'btree/numeric_ops', rngcanonical => '-', - rngsubdiff => 'numrange_subdiff' }, + rngmultitypid => 'nummultirange', rngsubopc => 'btree/numeric_ops', + rngcanonical => '-', rngsubdiff => 'numrange_subdiff' }, { rngtypid => 'tsrange', rngsubtype => 'timestamp', - rngsubopc => 'btree/timestamp_ops', rngcanonical => '-', - rngsubdiff => 'tsrange_subdiff' }, + rngmultitypid => 'tsmultirange', rngsubopc => 'btree/timestamp_ops', + rngcanonical => '-', rngsubdiff => 'tsrange_subdiff' }, { rngtypid => 'tstzrange', rngsubtype => 'timestamptz', - rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-', - rngsubdiff => 'tstzrange_subdiff' }, -{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops', + rngmultitypid => 'tstzmultirange', rngsubopc => 'btree/timestamptz_ops', + rngcanonical => '-', rngsubdiff => 'tstzrange_subdiff' }, +{ rngtypid => 'daterange', rngsubtype => 'date', + rngmultitypid => 'datemultirange', rngsubopc => 'btree/date_ops', rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' }, -{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops', +{ rngtypid => 'int8range', rngsubtype => 'int8', + rngmultitypid => 'int8multirange', rngsubopc => 'btree/int8_ops', rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' }, ] diff --git a/codegen/src/pg_type.dat b/codegen/src/pg_type.dat index e8be00083..df4587946 100644 --- a/codegen/src/pg_type.dat +++ b/codegen/src/pg_type.dat @@ -3,7 +3,7 @@ # pg_type.dat # Initial contents of the pg_type system catalog. # -# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group +# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # # src/include/catalog/pg_type.dat @@ -15,14 +15,10 @@ # For types used in the system catalogs, make sure the values here match # TypInfo[] in bootstrap.c. -# OID symbol macro names for pg_type OIDs are generated by genbki.pl -# according to the following rule, so you don't need to specify them -# here: +# OID symbol macro names for pg_type OIDs are not specified here because +# they are generated by genbki.pl according to the following rule: # foo_bar -> FOO_BAROID # _foo_bar -> FOO_BARARRAYOID -# -# The only oid_symbol entries in this file are for names that don't match -# this rule, and are grandfathered in. # To autogenerate an array type, add 'array_type_oid => 'nnnn' to the element # type, which will instruct genbki.pl to generate a BKI entry for it. @@ -46,15 +42,16 @@ typinput => 'byteain', typoutput => 'byteaout', typreceive => 'bytearecv', typsend => 'byteasend', typalign => 'i', typstorage => 'x' }, { oid => '18', array_type_oid => '1002', descr => 'single character', - typname => 'char', typlen => '1', typbyval => 't', typcategory => 'S', + typname => 'char', typlen => '1', typbyval => 't', typcategory => 'Z', typinput => 'charin', typoutput => 'charout', typreceive => 'charrecv', typsend => 'charsend', typalign => 'c' }, { oid => '19', array_type_oid => '1003', descr => '63-byte type for storing system identifiers', typname => 'name', typlen => 'NAMEDATALEN', typbyval => 'f', - typcategory => 'S', typelem => 'char', typinput => 'namein', - typoutput => 'nameout', typreceive => 'namerecv', typsend => 'namesend', - typalign => 'c', typcollation => 'C' }, + typcategory => 'S', typsubscript => 'raw_array_subscript_handler', + typelem => 'char', typinput => 'namein', typoutput => 'nameout', + typreceive => 'namerecv', typsend => 'namesend', typalign => 'c', + typcollation => 'C' }, { oid => '20', array_type_oid => '1016', descr => '~18 digit integer, 8-byte storage', typname => 'int8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL', @@ -68,7 +65,8 @@ { oid => '22', array_type_oid => '1006', descr => 'array of int2, used in system tables', typname => 'int2vector', typlen => '-1', typbyval => 'f', typcategory => 'A', - typelem => 'int2', typinput => 'int2vectorin', typoutput => 'int2vectorout', + typsubscript => 'array_subscript_handler', typelem => 'int2', + typinput => 'int2vectorin', typoutput => 'int2vectorout', typreceive => 'int2vectorrecv', typsend => 'int2vectorsend', typalign => 'i' }, { oid => '23', array_type_oid => '1007', @@ -108,27 +106,28 @@ { oid => '30', array_type_oid => '1013', descr => 'array of oids, used in system tables', typname => 'oidvector', typlen => '-1', typbyval => 'f', typcategory => 'A', - typelem => 'oid', typinput => 'oidvectorin', typoutput => 'oidvectorout', + typsubscript => 'array_subscript_handler', typelem => 'oid', + typinput => 'oidvectorin', typoutput => 'oidvectorout', typreceive => 'oidvectorrecv', typsend => 'oidvectorsend', typalign => 'i' }, # hand-built rowtype entries for bootstrapped catalogs # NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations -{ oid => '71', +{ oid => '71', array_type_oid => '210', typname => 'pg_type', typlen => '-1', typbyval => 'f', typtype => 'c', typcategory => 'C', typrelid => 'pg_type', typinput => 'record_in', typoutput => 'record_out', typreceive => 'record_recv', typsend => 'record_send', typalign => 'd', typstorage => 'x' }, -{ oid => '75', +{ oid => '75', array_type_oid => '270', typname => 'pg_attribute', typlen => '-1', typbyval => 'f', typtype => 'c', typcategory => 'C', typrelid => 'pg_attribute', typinput => 'record_in', typoutput => 'record_out', typreceive => 'record_recv', typsend => 'record_send', typalign => 'd', typstorage => 'x' }, -{ oid => '81', +{ oid => '81', array_type_oid => '272', typname => 'pg_proc', typlen => '-1', typbyval => 'f', typtype => 'c', typcategory => 'C', typrelid => 'pg_proc', typinput => 'record_in', typoutput => 'record_out', typreceive => 'record_recv', typsend => 'record_send', typalign => 'd', typstorage => 'x' }, -{ oid => '83', +{ oid => '83', array_type_oid => '273', typname => 'pg_class', typlen => '-1', typbyval => 'f', typtype => 'c', typcategory => 'C', typrelid => 'pg_class', typinput => 'record_in', typoutput => 'record_out', typreceive => 'record_recv', @@ -144,35 +143,30 @@ typname => 'xml', typlen => '-1', typbyval => 'f', typcategory => 'U', typinput => 'xml_in', typoutput => 'xml_out', typreceive => 'xml_recv', typsend => 'xml_send', typalign => 'i', typstorage => 'x' }, -{ oid => '194', oid_symbol => 'PGNODETREEOID', - descr => 'string representing an internal node tree', +{ oid => '194', descr => 'string representing an internal node tree', typname => 'pg_node_tree', typlen => '-1', typbyval => 'f', - typcategory => 'S', typinput => 'pg_node_tree_in', + typcategory => 'Z', typinput => 'pg_node_tree_in', typoutput => 'pg_node_tree_out', typreceive => 'pg_node_tree_recv', typsend => 'pg_node_tree_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, -{ oid => '3361', oid_symbol => 'PGNDISTINCTOID', - descr => 'multivariate ndistinct coefficients', +{ oid => '3361', descr => 'multivariate ndistinct coefficients', typname => 'pg_ndistinct', typlen => '-1', typbyval => 'f', - typcategory => 'S', typinput => 'pg_ndistinct_in', + typcategory => 'Z', typinput => 'pg_ndistinct_in', typoutput => 'pg_ndistinct_out', typreceive => 'pg_ndistinct_recv', typsend => 'pg_ndistinct_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, -{ oid => '3402', oid_symbol => 'PGDEPENDENCIESOID', - descr => 'multivariate dependencies', +{ oid => '3402', descr => 'multivariate dependencies', typname => 'pg_dependencies', typlen => '-1', typbyval => 'f', - typcategory => 'S', typinput => 'pg_dependencies_in', + typcategory => 'Z', typinput => 'pg_dependencies_in', typoutput => 'pg_dependencies_out', typreceive => 'pg_dependencies_recv', typsend => 'pg_dependencies_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, -{ oid => '5017', oid_symbol => 'PGMCVLISTOID', - descr => 'multivariate MCV list', - typname => 'pg_mcv_list', typlen => '-1', typbyval => 'f', typcategory => 'S', +{ oid => '5017', descr => 'multivariate MCV list', + typname => 'pg_mcv_list', typlen => '-1', typbyval => 'f', typcategory => 'Z', typinput => 'pg_mcv_list_in', typoutput => 'pg_mcv_list_out', typreceive => 'pg_mcv_list_recv', typsend => 'pg_mcv_list_send', typalign => 'i', typstorage => 'x', typcollation => 'default' }, -{ oid => '32', oid_symbol => 'PGDDLCOMMANDOID', - descr => 'internal type for passing CollectedCommand', +{ oid => '32', descr => 'internal type for passing CollectedCommand', typname => 'pg_ddl_command', typlen => 'SIZEOF_POINTER', typbyval => 't', typtype => 'p', typcategory => 'P', typinput => 'pg_ddl_command_in', typoutput => 'pg_ddl_command_out', typreceive => 'pg_ddl_command_recv', @@ -187,13 +181,15 @@ { oid => '600', array_type_oid => '1017', descr => 'geometric point \'(x, y)\'', typname => 'point', typlen => '16', typbyval => 'f', typcategory => 'G', - typelem => 'float8', typinput => 'point_in', typoutput => 'point_out', - typreceive => 'point_recv', typsend => 'point_send', typalign => 'd' }, + typsubscript => 'raw_array_subscript_handler', typelem => 'float8', + typinput => 'point_in', typoutput => 'point_out', typreceive => 'point_recv', + typsend => 'point_send', typalign => 'd' }, { oid => '601', array_type_oid => '1018', descr => 'geometric line segment \'(pt1,pt2)\'', typname => 'lseg', typlen => '32', typbyval => 'f', typcategory => 'G', - typelem => 'point', typinput => 'lseg_in', typoutput => 'lseg_out', - typreceive => 'lseg_recv', typsend => 'lseg_send', typalign => 'd' }, + typsubscript => 'raw_array_subscript_handler', typelem => 'point', + typinput => 'lseg_in', typoutput => 'lseg_out', typreceive => 'lseg_recv', + typsend => 'lseg_send', typalign => 'd' }, { oid => '602', array_type_oid => '1019', descr => 'geometric path \'(pt1,...)\'', typname => 'path', typlen => '-1', typbyval => 'f', typcategory => 'G', @@ -202,9 +198,9 @@ { oid => '603', array_type_oid => '1020', descr => 'geometric box \'(lower left,upper right)\'', typname => 'box', typlen => '32', typbyval => 'f', typcategory => 'G', - typdelim => ';', typelem => 'point', typinput => 'box_in', - typoutput => 'box_out', typreceive => 'box_recv', typsend => 'box_send', - typalign => 'd' }, + typdelim => ';', typsubscript => 'raw_array_subscript_handler', + typelem => 'point', typinput => 'box_in', typoutput => 'box_out', + typreceive => 'box_recv', typsend => 'box_send', typalign => 'd' }, { oid => '604', array_type_oid => '1027', descr => 'geometric polygon \'(pt1,...)\'', typname => 'polygon', typlen => '-1', typbyval => 'f', typcategory => 'G', @@ -212,8 +208,9 @@ typsend => 'poly_send', typalign => 'd', typstorage => 'x' }, { oid => '628', array_type_oid => '629', descr => 'geometric line', typname => 'line', typlen => '24', typbyval => 'f', typcategory => 'G', - typelem => 'float8', typinput => 'line_in', typoutput => 'line_out', - typreceive => 'line_recv', typsend => 'line_send', typalign => 'd' }, + typsubscript => 'raw_array_subscript_handler', typelem => 'float8', + typinput => 'line_in', typoutput => 'line_out', typreceive => 'line_recv', + typsend => 'line_send', typalign => 'd' }, # OIDS 700 - 799 @@ -237,7 +234,7 @@ typname => 'circle', typlen => '24', typbyval => 'f', typcategory => 'G', typinput => 'circle_in', typoutput => 'circle_out', typreceive => 'circle_recv', typsend => 'circle_send', typalign => 'd' }, -{ oid => '790', oid_symbol => 'CASHOID', array_type_oid => '791', +{ oid => '790', array_type_oid => '791', descr => 'monetary amounts, $d,ddd.cc', typname => 'money', typlen => '8', typbyval => 'FLOAT8PASSBYVAL', typcategory => 'N', typinput => 'cash_in', typoutput => 'cash_out', @@ -409,8 +406,7 @@ typsend => 'uuid_send', typalign => 'c' }, # pg_lsn -{ oid => '3220', oid_symbol => 'LSNOID', array_type_oid => '3221', - descr => 'PostgreSQL LSN datatype', +{ oid => '3220', array_type_oid => '3221', descr => 'PostgreSQL LSN datatype', typname => 'pg_lsn', typlen => '8', typbyval => 'FLOAT8PASSBYVAL', typcategory => 'U', typinput => 'pg_lsn_in', typoutput => 'pg_lsn_out', typreceive => 'pg_lsn_recv', typsend => 'pg_lsn_send', typalign => 'd' }, @@ -447,8 +443,9 @@ # jsonb { oid => '3802', array_type_oid => '3807', descr => 'Binary JSON', typname => 'jsonb', typlen => '-1', typbyval => 'f', typcategory => 'U', - typinput => 'jsonb_in', typoutput => 'jsonb_out', typreceive => 'jsonb_recv', - typsend => 'jsonb_send', typalign => 'i', typstorage => 'x' }, + typsubscript => 'jsonb_subscript_handler', typinput => 'jsonb_in', + typoutput => 'jsonb_out', typreceive => 'jsonb_recv', typsend => 'jsonb_send', + typalign => 'i', typstorage => 'x' }, { oid => '4072', array_type_oid => '4073', descr => 'JSON path', typname => 'jsonpath', typlen => '-1', typbyval => 'f', typcategory => 'U', typinput => 'jsonpath_in', typoutput => 'jsonpath_out', @@ -500,6 +497,46 @@ typreceive => 'range_recv', typsend => 'range_send', typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' }, +# multirange types +{ oid => '4451', array_type_oid => '6150', descr => 'multirange of integers', + typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm', + typcategory => 'R', typinput => 'multirange_in', + typoutput => 'multirange_out', typreceive => 'multirange_recv', + typsend => 'multirange_send', typanalyze => 'multirange_typanalyze', + typalign => 'i', typstorage => 'x' }, +{ oid => '4532', array_type_oid => '6151', descr => 'multirange of numerics', + typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm', + typcategory => 'R', typinput => 'multirange_in', + typoutput => 'multirange_out', typreceive => 'multirange_recv', + typsend => 'multirange_send', typanalyze => 'multirange_typanalyze', + typalign => 'i', typstorage => 'x' }, +{ oid => '4533', array_type_oid => '6152', + descr => 'multirange of timestamps without time zone', + typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm', + typcategory => 'R', typinput => 'multirange_in', + typoutput => 'multirange_out', typreceive => 'multirange_recv', + typsend => 'multirange_send', typanalyze => 'multirange_typanalyze', + typalign => 'd', typstorage => 'x' }, +{ oid => '4534', array_type_oid => '6153', + descr => 'multirange of timestamps with time zone', + typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm', + typcategory => 'R', typinput => 'multirange_in', + typoutput => 'multirange_out', typreceive => 'multirange_recv', + typsend => 'multirange_send', typanalyze => 'multirange_typanalyze', + typalign => 'd', typstorage => 'x' }, +{ oid => '4535', array_type_oid => '6155', descr => 'multirange of dates', + typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm', + typcategory => 'R', typinput => 'multirange_in', + typoutput => 'multirange_out', typreceive => 'multirange_recv', + typsend => 'multirange_send', typanalyze => 'multirange_typanalyze', + typalign => 'i', typstorage => 'x' }, +{ oid => '4536', array_type_oid => '6157', descr => 'multirange of bigints', + typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm', + typcategory => 'R', typinput => 'multirange_in', + typoutput => 'multirange_out', typreceive => 'multirange_recv', + typsend => 'multirange_send', typanalyze => 'multirange_typanalyze', + typalign => 'd', typstorage => 'x' }, + # pseudo-types # types with typtype='p' represent various special cases in the type system. # These cannot be used to define table columns, but are valid as function @@ -517,8 +554,9 @@ # Arrays of records have typcategory P, so they can't be autogenerated. { oid => '2287', typname => '_record', typlen => '-1', typbyval => 'f', typtype => 'p', - typcategory => 'P', typelem => 'record', typinput => 'array_in', - typoutput => 'array_out', typreceive => 'array_recv', typsend => 'array_send', + typcategory => 'P', typsubscript => 'array_subscript_handler', + typelem => 'record', typinput => 'array_in', typoutput => 'array_out', + typreceive => 'array_recv', typsend => 'array_send', typanalyze => 'array_typanalyze', typalign => 'd', typstorage => 'x' }, { oid => '2275', array_type_oid => '1263', descr => 'C-style string', typname => 'cstring', typlen => '-2', typbyval => 'f', typtype => 'p', @@ -542,7 +580,7 @@ typname => 'trigger', typlen => '4', typbyval => 't', typtype => 'p', typcategory => 'P', typinput => 'trigger_in', typoutput => 'trigger_out', typreceive => '-', typsend => '-', typalign => 'i' }, -{ oid => '3838', oid_symbol => 'EVTTRIGGEROID', +{ oid => '3838', descr => 'pseudo-type for the result of an event trigger function', typname => 'event_trigger', typlen => '4', typbyval => 't', typtype => 'p', typcategory => 'P', typinput => 'event_trigger_in', @@ -629,5 +667,29 @@ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in', typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' }, - +{ oid => '4537', + descr => 'pseudo-type representing a polymorphic base type that is a multirange', + typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p', + typcategory => 'P', typinput => 'anymultirange_in', + typoutput => 'anymultirange_out', typreceive => '-', typsend => '-', + typalign => 'd', typstorage => 'x' }, +{ oid => '4538', + descr => 'pseudo-type representing a multirange over a polymorphic common type', + typname => 'anycompatiblemultirange', typlen => '-1', typbyval => 'f', + typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in', + typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-', + typalign => 'd', typstorage => 'x' }, +{ oid => '4600', descr => 'BRIN bloom summary', + typname => 'pg_brin_bloom_summary', typlen => '-1', typbyval => 'f', + typcategory => 'Z', typinput => 'brin_bloom_summary_in', + typoutput => 'brin_bloom_summary_out', + typreceive => 'brin_bloom_summary_recv', typsend => 'brin_bloom_summary_send', + typalign => 'i', typstorage => 'x', typcollation => 'default' }, +{ oid => '4601', descr => 'BRIN minmax-multi summary', + typname => 'pg_brin_minmax_multi_summary', typlen => '-1', typbyval => 'f', + typcategory => 'Z', typinput => 'brin_minmax_multi_summary_in', + typoutput => 'brin_minmax_multi_summary_out', + typreceive => 'brin_minmax_multi_summary_recv', + typsend => 'brin_minmax_multi_summary_send', typalign => 'i', + typstorage => 'x', typcollation => 'default' }, ] From f413e66b14f9b4574b69005a4671441ea10b2e6e Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Mon, 21 Nov 2022 15:24:21 -0500 Subject: [PATCH 014/207] Add support for multiranges --- codegen/src/type_gen.rs | 15 +- postgres-types/src/type_gen.rs | 840 +++++++++++++++++++++------ tokio-postgres/src/error/sqlstate.rs | 572 +++++++++--------- 3 files changed, 971 insertions(+), 456 deletions(-) diff --git a/codegen/src/type_gen.rs b/codegen/src/type_gen.rs index 7e92e062a..249c5530a 100644 --- a/codegen/src/type_gen.rs +++ b/codegen/src/type_gen.rs @@ -185,6 +185,15 @@ fn parse_types() -> BTreeMap { ) }) .collect::>(); + let multi_range_elements = raw_ranges + .iter() + .map(|m| { + ( + oids_by_name[&*m["rngmultitypid"]], + oids_by_name[&*m["rngsubtype"]], + ) + }) + .collect::>(); let range_vector_re = Regex::new("(range|vector)$").unwrap(); let array_re = Regex::new("^_(.*)").unwrap(); @@ -209,7 +218,11 @@ fn parse_types() -> BTreeMap { } let element = match &*kind { - "R" => range_elements[&oid], + "R" => match &*raw_type["typtype"] { + "r" => range_elements[&oid], + "m" => multi_range_elements[&oid], + typtype => panic!("invalid range typtype {}", typtype), + } "A" => oids_by_name[&raw_type["typelem"]], _ => 0, }; diff --git a/postgres-types/src/type_gen.rs b/postgres-types/src/type_gen.rs index 85978096a..901fb46e0 100644 --- a/postgres-types/src/type_gen.rs +++ b/postgres-types/src/type_gen.rs @@ -1,7 +1,7 @@ // Autogenerated file - DO NOT EDIT use std::sync::Arc; -use crate::{Kind, Oid, Type}; +use crate::{Type, Oid, Kind}; #[derive(PartialEq, Eq, Debug, Hash)] pub struct Other { @@ -174,6 +174,16 @@ pub enum Inner { RegroleArray, Regcollation, RegcollationArray, + Int4multiRange, + NummultiRange, + TsmultiRange, + TstzmultiRange, + DatemultiRange, + Int8multiRange, + AnymultiRange, + AnycompatiblemultiRange, + PgBrinBloomSummary, + PgBrinMinmaxMultiSummary, PgMcvList, PgSnapshot, PgSnapshotArray, @@ -182,6 +192,12 @@ pub enum Inner { Anycompatiblearray, Anycompatiblenonarray, AnycompatibleRange, + Int4multiRangeArray, + NummultiRangeArray, + TsmultiRangeArray, + TstzmultiRangeArray, + DatemultiRangeArray, + Int8multiRangeArray, Other(Arc), } @@ -349,6 +365,16 @@ impl Inner { 4097 => Some(Inner::RegroleArray), 4191 => Some(Inner::Regcollation), 4192 => Some(Inner::RegcollationArray), + 4451 => Some(Inner::Int4multiRange), + 4532 => Some(Inner::NummultiRange), + 4533 => Some(Inner::TsmultiRange), + 4534 => Some(Inner::TstzmultiRange), + 4535 => Some(Inner::DatemultiRange), + 4536 => Some(Inner::Int8multiRange), + 4537 => Some(Inner::AnymultiRange), + 4538 => Some(Inner::AnycompatiblemultiRange), + 4600 => Some(Inner::PgBrinBloomSummary), + 4601 => Some(Inner::PgBrinMinmaxMultiSummary), 5017 => Some(Inner::PgMcvList), 5038 => Some(Inner::PgSnapshot), 5039 => Some(Inner::PgSnapshotArray), @@ -357,6 +383,12 @@ impl Inner { 5078 => Some(Inner::Anycompatiblearray), 5079 => Some(Inner::Anycompatiblenonarray), 5080 => Some(Inner::AnycompatibleRange), + 6150 => Some(Inner::Int4multiRangeArray), + 6151 => Some(Inner::NummultiRangeArray), + 6152 => Some(Inner::TsmultiRangeArray), + 6153 => Some(Inner::TstzmultiRangeArray), + 6155 => Some(Inner::DatemultiRangeArray), + 6157 => Some(Inner::Int8multiRangeArray), _ => None, } } @@ -524,6 +556,16 @@ impl Inner { Inner::RegroleArray => 4097, Inner::Regcollation => 4191, Inner::RegcollationArray => 4192, + Inner::Int4multiRange => 4451, + Inner::NummultiRange => 4532, + Inner::TsmultiRange => 4533, + Inner::TstzmultiRange => 4534, + Inner::DatemultiRange => 4535, + Inner::Int8multiRange => 4536, + Inner::AnymultiRange => 4537, + Inner::AnycompatiblemultiRange => 4538, + Inner::PgBrinBloomSummary => 4600, + Inner::PgBrinMinmaxMultiSummary => 4601, Inner::PgMcvList => 5017, Inner::PgSnapshot => 5038, Inner::PgSnapshotArray => 5039, @@ -532,181 +574,573 @@ impl Inner { Inner::Anycompatiblearray => 5078, Inner::Anycompatiblenonarray => 5079, Inner::AnycompatibleRange => 5080, + Inner::Int4multiRangeArray => 6150, + Inner::NummultiRangeArray => 6151, + Inner::TsmultiRangeArray => 6152, + Inner::TstzmultiRangeArray => 6153, + Inner::DatemultiRangeArray => 6155, + Inner::Int8multiRangeArray => 6157, Inner::Other(ref u) => u.oid, } } pub fn kind(&self) -> &Kind { match *self { - Inner::Bool => &Kind::Simple, - Inner::Bytea => &Kind::Simple, - Inner::Char => &Kind::Simple, - Inner::Name => &Kind::Simple, - Inner::Int8 => &Kind::Simple, - Inner::Int2 => &Kind::Simple, - Inner::Int2Vector => &Kind::Array(Type(Inner::Int2)), - Inner::Int4 => &Kind::Simple, - Inner::Regproc => &Kind::Simple, - Inner::Text => &Kind::Simple, - Inner::Oid => &Kind::Simple, - Inner::Tid => &Kind::Simple, - Inner::Xid => &Kind::Simple, - Inner::Cid => &Kind::Simple, - Inner::OidVector => &Kind::Array(Type(Inner::Oid)), - Inner::PgDdlCommand => &Kind::Pseudo, - Inner::Json => &Kind::Simple, - Inner::Xml => &Kind::Simple, - Inner::XmlArray => &Kind::Array(Type(Inner::Xml)), - Inner::PgNodeTree => &Kind::Simple, - Inner::JsonArray => &Kind::Array(Type(Inner::Json)), - Inner::TableAmHandler => &Kind::Pseudo, - Inner::Xid8Array => &Kind::Array(Type(Inner::Xid8)), - Inner::IndexAmHandler => &Kind::Pseudo, - Inner::Point => &Kind::Simple, - Inner::Lseg => &Kind::Simple, - Inner::Path => &Kind::Simple, - Inner::Box => &Kind::Simple, - Inner::Polygon => &Kind::Simple, - Inner::Line => &Kind::Simple, - Inner::LineArray => &Kind::Array(Type(Inner::Line)), - Inner::Cidr => &Kind::Simple, - Inner::CidrArray => &Kind::Array(Type(Inner::Cidr)), - Inner::Float4 => &Kind::Simple, - Inner::Float8 => &Kind::Simple, - Inner::Unknown => &Kind::Simple, - Inner::Circle => &Kind::Simple, - Inner::CircleArray => &Kind::Array(Type(Inner::Circle)), - Inner::Macaddr8 => &Kind::Simple, - Inner::Macaddr8Array => &Kind::Array(Type(Inner::Macaddr8)), - Inner::Money => &Kind::Simple, - Inner::MoneyArray => &Kind::Array(Type(Inner::Money)), - Inner::Macaddr => &Kind::Simple, - Inner::Inet => &Kind::Simple, - Inner::BoolArray => &Kind::Array(Type(Inner::Bool)), - Inner::ByteaArray => &Kind::Array(Type(Inner::Bytea)), - Inner::CharArray => &Kind::Array(Type(Inner::Char)), - Inner::NameArray => &Kind::Array(Type(Inner::Name)), - Inner::Int2Array => &Kind::Array(Type(Inner::Int2)), - Inner::Int2VectorArray => &Kind::Array(Type(Inner::Int2Vector)), - Inner::Int4Array => &Kind::Array(Type(Inner::Int4)), - Inner::RegprocArray => &Kind::Array(Type(Inner::Regproc)), - Inner::TextArray => &Kind::Array(Type(Inner::Text)), - Inner::TidArray => &Kind::Array(Type(Inner::Tid)), - Inner::XidArray => &Kind::Array(Type(Inner::Xid)), - Inner::CidArray => &Kind::Array(Type(Inner::Cid)), - Inner::OidVectorArray => &Kind::Array(Type(Inner::OidVector)), - Inner::BpcharArray => &Kind::Array(Type(Inner::Bpchar)), - Inner::VarcharArray => &Kind::Array(Type(Inner::Varchar)), - Inner::Int8Array => &Kind::Array(Type(Inner::Int8)), - Inner::PointArray => &Kind::Array(Type(Inner::Point)), - Inner::LsegArray => &Kind::Array(Type(Inner::Lseg)), - Inner::PathArray => &Kind::Array(Type(Inner::Path)), - Inner::BoxArray => &Kind::Array(Type(Inner::Box)), - Inner::Float4Array => &Kind::Array(Type(Inner::Float4)), - Inner::Float8Array => &Kind::Array(Type(Inner::Float8)), - Inner::PolygonArray => &Kind::Array(Type(Inner::Polygon)), - Inner::OidArray => &Kind::Array(Type(Inner::Oid)), - Inner::Aclitem => &Kind::Simple, - Inner::AclitemArray => &Kind::Array(Type(Inner::Aclitem)), - Inner::MacaddrArray => &Kind::Array(Type(Inner::Macaddr)), - Inner::InetArray => &Kind::Array(Type(Inner::Inet)), - Inner::Bpchar => &Kind::Simple, - Inner::Varchar => &Kind::Simple, - Inner::Date => &Kind::Simple, - Inner::Time => &Kind::Simple, - Inner::Timestamp => &Kind::Simple, - Inner::TimestampArray => &Kind::Array(Type(Inner::Timestamp)), - Inner::DateArray => &Kind::Array(Type(Inner::Date)), - Inner::TimeArray => &Kind::Array(Type(Inner::Time)), - Inner::Timestamptz => &Kind::Simple, - Inner::TimestamptzArray => &Kind::Array(Type(Inner::Timestamptz)), - Inner::Interval => &Kind::Simple, - Inner::IntervalArray => &Kind::Array(Type(Inner::Interval)), - Inner::NumericArray => &Kind::Array(Type(Inner::Numeric)), - Inner::CstringArray => &Kind::Array(Type(Inner::Cstring)), - Inner::Timetz => &Kind::Simple, - Inner::TimetzArray => &Kind::Array(Type(Inner::Timetz)), - Inner::Bit => &Kind::Simple, - Inner::BitArray => &Kind::Array(Type(Inner::Bit)), - Inner::Varbit => &Kind::Simple, - Inner::VarbitArray => &Kind::Array(Type(Inner::Varbit)), - Inner::Numeric => &Kind::Simple, - Inner::Refcursor => &Kind::Simple, - Inner::RefcursorArray => &Kind::Array(Type(Inner::Refcursor)), - Inner::Regprocedure => &Kind::Simple, - Inner::Regoper => &Kind::Simple, - Inner::Regoperator => &Kind::Simple, - Inner::Regclass => &Kind::Simple, - Inner::Regtype => &Kind::Simple, - Inner::RegprocedureArray => &Kind::Array(Type(Inner::Regprocedure)), - Inner::RegoperArray => &Kind::Array(Type(Inner::Regoper)), - Inner::RegoperatorArray => &Kind::Array(Type(Inner::Regoperator)), - Inner::RegclassArray => &Kind::Array(Type(Inner::Regclass)), - Inner::RegtypeArray => &Kind::Array(Type(Inner::Regtype)), - Inner::Record => &Kind::Pseudo, - Inner::Cstring => &Kind::Pseudo, - Inner::Any => &Kind::Pseudo, - Inner::Anyarray => &Kind::Pseudo, - Inner::Void => &Kind::Pseudo, - Inner::Trigger => &Kind::Pseudo, - Inner::LanguageHandler => &Kind::Pseudo, - Inner::Internal => &Kind::Pseudo, - Inner::Anyelement => &Kind::Pseudo, - Inner::RecordArray => &Kind::Pseudo, - Inner::Anynonarray => &Kind::Pseudo, - Inner::TxidSnapshotArray => &Kind::Array(Type(Inner::TxidSnapshot)), - Inner::Uuid => &Kind::Simple, - Inner::UuidArray => &Kind::Array(Type(Inner::Uuid)), - Inner::TxidSnapshot => &Kind::Simple, - Inner::FdwHandler => &Kind::Pseudo, - Inner::PgLsn => &Kind::Simple, - Inner::PgLsnArray => &Kind::Array(Type(Inner::PgLsn)), - Inner::TsmHandler => &Kind::Pseudo, - Inner::PgNdistinct => &Kind::Simple, - Inner::PgDependencies => &Kind::Simple, - Inner::Anyenum => &Kind::Pseudo, - Inner::TsVector => &Kind::Simple, - Inner::Tsquery => &Kind::Simple, - Inner::GtsVector => &Kind::Simple, - Inner::TsVectorArray => &Kind::Array(Type(Inner::TsVector)), - Inner::GtsVectorArray => &Kind::Array(Type(Inner::GtsVector)), - Inner::TsqueryArray => &Kind::Array(Type(Inner::Tsquery)), - Inner::Regconfig => &Kind::Simple, - Inner::RegconfigArray => &Kind::Array(Type(Inner::Regconfig)), - Inner::Regdictionary => &Kind::Simple, - Inner::RegdictionaryArray => &Kind::Array(Type(Inner::Regdictionary)), - Inner::Jsonb => &Kind::Simple, - Inner::JsonbArray => &Kind::Array(Type(Inner::Jsonb)), - Inner::AnyRange => &Kind::Pseudo, - Inner::EventTrigger => &Kind::Pseudo, - Inner::Int4Range => &Kind::Range(Type(Inner::Int4)), - Inner::Int4RangeArray => &Kind::Array(Type(Inner::Int4Range)), - Inner::NumRange => &Kind::Range(Type(Inner::Numeric)), - Inner::NumRangeArray => &Kind::Array(Type(Inner::NumRange)), - Inner::TsRange => &Kind::Range(Type(Inner::Timestamp)), - Inner::TsRangeArray => &Kind::Array(Type(Inner::TsRange)), - Inner::TstzRange => &Kind::Range(Type(Inner::Timestamptz)), - Inner::TstzRangeArray => &Kind::Array(Type(Inner::TstzRange)), - Inner::DateRange => &Kind::Range(Type(Inner::Date)), - Inner::DateRangeArray => &Kind::Array(Type(Inner::DateRange)), - Inner::Int8Range => &Kind::Range(Type(Inner::Int8)), - Inner::Int8RangeArray => &Kind::Array(Type(Inner::Int8Range)), - Inner::Jsonpath => &Kind::Simple, - Inner::JsonpathArray => &Kind::Array(Type(Inner::Jsonpath)), - Inner::Regnamespace => &Kind::Simple, - Inner::RegnamespaceArray => &Kind::Array(Type(Inner::Regnamespace)), - Inner::Regrole => &Kind::Simple, - Inner::RegroleArray => &Kind::Array(Type(Inner::Regrole)), - Inner::Regcollation => &Kind::Simple, - Inner::RegcollationArray => &Kind::Array(Type(Inner::Regcollation)), - Inner::PgMcvList => &Kind::Simple, - Inner::PgSnapshot => &Kind::Simple, - Inner::PgSnapshotArray => &Kind::Array(Type(Inner::PgSnapshot)), - Inner::Xid8 => &Kind::Simple, - Inner::Anycompatible => &Kind::Pseudo, - Inner::Anycompatiblearray => &Kind::Pseudo, - Inner::Anycompatiblenonarray => &Kind::Pseudo, - Inner::AnycompatibleRange => &Kind::Pseudo, + Inner::Bool => { + &Kind::Simple + } + Inner::Bytea => { + &Kind::Simple + } + Inner::Char => { + &Kind::Simple + } + Inner::Name => { + &Kind::Simple + } + Inner::Int8 => { + &Kind::Simple + } + Inner::Int2 => { + &Kind::Simple + } + Inner::Int2Vector => { + &Kind::Array(Type(Inner::Int2)) + } + Inner::Int4 => { + &Kind::Simple + } + Inner::Regproc => { + &Kind::Simple + } + Inner::Text => { + &Kind::Simple + } + Inner::Oid => { + &Kind::Simple + } + Inner::Tid => { + &Kind::Simple + } + Inner::Xid => { + &Kind::Simple + } + Inner::Cid => { + &Kind::Simple + } + Inner::OidVector => { + &Kind::Array(Type(Inner::Oid)) + } + Inner::PgDdlCommand => { + &Kind::Pseudo + } + Inner::Json => { + &Kind::Simple + } + Inner::Xml => { + &Kind::Simple + } + Inner::XmlArray => { + &Kind::Array(Type(Inner::Xml)) + } + Inner::PgNodeTree => { + &Kind::Simple + } + Inner::JsonArray => { + &Kind::Array(Type(Inner::Json)) + } + Inner::TableAmHandler => { + &Kind::Pseudo + } + Inner::Xid8Array => { + &Kind::Array(Type(Inner::Xid8)) + } + Inner::IndexAmHandler => { + &Kind::Pseudo + } + Inner::Point => { + &Kind::Simple + } + Inner::Lseg => { + &Kind::Simple + } + Inner::Path => { + &Kind::Simple + } + Inner::Box => { + &Kind::Simple + } + Inner::Polygon => { + &Kind::Simple + } + Inner::Line => { + &Kind::Simple + } + Inner::LineArray => { + &Kind::Array(Type(Inner::Line)) + } + Inner::Cidr => { + &Kind::Simple + } + Inner::CidrArray => { + &Kind::Array(Type(Inner::Cidr)) + } + Inner::Float4 => { + &Kind::Simple + } + Inner::Float8 => { + &Kind::Simple + } + Inner::Unknown => { + &Kind::Simple + } + Inner::Circle => { + &Kind::Simple + } + Inner::CircleArray => { + &Kind::Array(Type(Inner::Circle)) + } + Inner::Macaddr8 => { + &Kind::Simple + } + Inner::Macaddr8Array => { + &Kind::Array(Type(Inner::Macaddr8)) + } + Inner::Money => { + &Kind::Simple + } + Inner::MoneyArray => { + &Kind::Array(Type(Inner::Money)) + } + Inner::Macaddr => { + &Kind::Simple + } + Inner::Inet => { + &Kind::Simple + } + Inner::BoolArray => { + &Kind::Array(Type(Inner::Bool)) + } + Inner::ByteaArray => { + &Kind::Array(Type(Inner::Bytea)) + } + Inner::CharArray => { + &Kind::Array(Type(Inner::Char)) + } + Inner::NameArray => { + &Kind::Array(Type(Inner::Name)) + } + Inner::Int2Array => { + &Kind::Array(Type(Inner::Int2)) + } + Inner::Int2VectorArray => { + &Kind::Array(Type(Inner::Int2Vector)) + } + Inner::Int4Array => { + &Kind::Array(Type(Inner::Int4)) + } + Inner::RegprocArray => { + &Kind::Array(Type(Inner::Regproc)) + } + Inner::TextArray => { + &Kind::Array(Type(Inner::Text)) + } + Inner::TidArray => { + &Kind::Array(Type(Inner::Tid)) + } + Inner::XidArray => { + &Kind::Array(Type(Inner::Xid)) + } + Inner::CidArray => { + &Kind::Array(Type(Inner::Cid)) + } + Inner::OidVectorArray => { + &Kind::Array(Type(Inner::OidVector)) + } + Inner::BpcharArray => { + &Kind::Array(Type(Inner::Bpchar)) + } + Inner::VarcharArray => { + &Kind::Array(Type(Inner::Varchar)) + } + Inner::Int8Array => { + &Kind::Array(Type(Inner::Int8)) + } + Inner::PointArray => { + &Kind::Array(Type(Inner::Point)) + } + Inner::LsegArray => { + &Kind::Array(Type(Inner::Lseg)) + } + Inner::PathArray => { + &Kind::Array(Type(Inner::Path)) + } + Inner::BoxArray => { + &Kind::Array(Type(Inner::Box)) + } + Inner::Float4Array => { + &Kind::Array(Type(Inner::Float4)) + } + Inner::Float8Array => { + &Kind::Array(Type(Inner::Float8)) + } + Inner::PolygonArray => { + &Kind::Array(Type(Inner::Polygon)) + } + Inner::OidArray => { + &Kind::Array(Type(Inner::Oid)) + } + Inner::Aclitem => { + &Kind::Simple + } + Inner::AclitemArray => { + &Kind::Array(Type(Inner::Aclitem)) + } + Inner::MacaddrArray => { + &Kind::Array(Type(Inner::Macaddr)) + } + Inner::InetArray => { + &Kind::Array(Type(Inner::Inet)) + } + Inner::Bpchar => { + &Kind::Simple + } + Inner::Varchar => { + &Kind::Simple + } + Inner::Date => { + &Kind::Simple + } + Inner::Time => { + &Kind::Simple + } + Inner::Timestamp => { + &Kind::Simple + } + Inner::TimestampArray => { + &Kind::Array(Type(Inner::Timestamp)) + } + Inner::DateArray => { + &Kind::Array(Type(Inner::Date)) + } + Inner::TimeArray => { + &Kind::Array(Type(Inner::Time)) + } + Inner::Timestamptz => { + &Kind::Simple + } + Inner::TimestamptzArray => { + &Kind::Array(Type(Inner::Timestamptz)) + } + Inner::Interval => { + &Kind::Simple + } + Inner::IntervalArray => { + &Kind::Array(Type(Inner::Interval)) + } + Inner::NumericArray => { + &Kind::Array(Type(Inner::Numeric)) + } + Inner::CstringArray => { + &Kind::Array(Type(Inner::Cstring)) + } + Inner::Timetz => { + &Kind::Simple + } + Inner::TimetzArray => { + &Kind::Array(Type(Inner::Timetz)) + } + Inner::Bit => { + &Kind::Simple + } + Inner::BitArray => { + &Kind::Array(Type(Inner::Bit)) + } + Inner::Varbit => { + &Kind::Simple + } + Inner::VarbitArray => { + &Kind::Array(Type(Inner::Varbit)) + } + Inner::Numeric => { + &Kind::Simple + } + Inner::Refcursor => { + &Kind::Simple + } + Inner::RefcursorArray => { + &Kind::Array(Type(Inner::Refcursor)) + } + Inner::Regprocedure => { + &Kind::Simple + } + Inner::Regoper => { + &Kind::Simple + } + Inner::Regoperator => { + &Kind::Simple + } + Inner::Regclass => { + &Kind::Simple + } + Inner::Regtype => { + &Kind::Simple + } + Inner::RegprocedureArray => { + &Kind::Array(Type(Inner::Regprocedure)) + } + Inner::RegoperArray => { + &Kind::Array(Type(Inner::Regoper)) + } + Inner::RegoperatorArray => { + &Kind::Array(Type(Inner::Regoperator)) + } + Inner::RegclassArray => { + &Kind::Array(Type(Inner::Regclass)) + } + Inner::RegtypeArray => { + &Kind::Array(Type(Inner::Regtype)) + } + Inner::Record => { + &Kind::Pseudo + } + Inner::Cstring => { + &Kind::Pseudo + } + Inner::Any => { + &Kind::Pseudo + } + Inner::Anyarray => { + &Kind::Pseudo + } + Inner::Void => { + &Kind::Pseudo + } + Inner::Trigger => { + &Kind::Pseudo + } + Inner::LanguageHandler => { + &Kind::Pseudo + } + Inner::Internal => { + &Kind::Pseudo + } + Inner::Anyelement => { + &Kind::Pseudo + } + Inner::RecordArray => { + &Kind::Pseudo + } + Inner::Anynonarray => { + &Kind::Pseudo + } + Inner::TxidSnapshotArray => { + &Kind::Array(Type(Inner::TxidSnapshot)) + } + Inner::Uuid => { + &Kind::Simple + } + Inner::UuidArray => { + &Kind::Array(Type(Inner::Uuid)) + } + Inner::TxidSnapshot => { + &Kind::Simple + } + Inner::FdwHandler => { + &Kind::Pseudo + } + Inner::PgLsn => { + &Kind::Simple + } + Inner::PgLsnArray => { + &Kind::Array(Type(Inner::PgLsn)) + } + Inner::TsmHandler => { + &Kind::Pseudo + } + Inner::PgNdistinct => { + &Kind::Simple + } + Inner::PgDependencies => { + &Kind::Simple + } + Inner::Anyenum => { + &Kind::Pseudo + } + Inner::TsVector => { + &Kind::Simple + } + Inner::Tsquery => { + &Kind::Simple + } + Inner::GtsVector => { + &Kind::Simple + } + Inner::TsVectorArray => { + &Kind::Array(Type(Inner::TsVector)) + } + Inner::GtsVectorArray => { + &Kind::Array(Type(Inner::GtsVector)) + } + Inner::TsqueryArray => { + &Kind::Array(Type(Inner::Tsquery)) + } + Inner::Regconfig => { + &Kind::Simple + } + Inner::RegconfigArray => { + &Kind::Array(Type(Inner::Regconfig)) + } + Inner::Regdictionary => { + &Kind::Simple + } + Inner::RegdictionaryArray => { + &Kind::Array(Type(Inner::Regdictionary)) + } + Inner::Jsonb => { + &Kind::Simple + } + Inner::JsonbArray => { + &Kind::Array(Type(Inner::Jsonb)) + } + Inner::AnyRange => { + &Kind::Pseudo + } + Inner::EventTrigger => { + &Kind::Pseudo + } + Inner::Int4Range => { + &Kind::Range(Type(Inner::Int4)) + } + Inner::Int4RangeArray => { + &Kind::Array(Type(Inner::Int4Range)) + } + Inner::NumRange => { + &Kind::Range(Type(Inner::Numeric)) + } + Inner::NumRangeArray => { + &Kind::Array(Type(Inner::NumRange)) + } + Inner::TsRange => { + &Kind::Range(Type(Inner::Timestamp)) + } + Inner::TsRangeArray => { + &Kind::Array(Type(Inner::TsRange)) + } + Inner::TstzRange => { + &Kind::Range(Type(Inner::Timestamptz)) + } + Inner::TstzRangeArray => { + &Kind::Array(Type(Inner::TstzRange)) + } + Inner::DateRange => { + &Kind::Range(Type(Inner::Date)) + } + Inner::DateRangeArray => { + &Kind::Array(Type(Inner::DateRange)) + } + Inner::Int8Range => { + &Kind::Range(Type(Inner::Int8)) + } + Inner::Int8RangeArray => { + &Kind::Array(Type(Inner::Int8Range)) + } + Inner::Jsonpath => { + &Kind::Simple + } + Inner::JsonpathArray => { + &Kind::Array(Type(Inner::Jsonpath)) + } + Inner::Regnamespace => { + &Kind::Simple + } + Inner::RegnamespaceArray => { + &Kind::Array(Type(Inner::Regnamespace)) + } + Inner::Regrole => { + &Kind::Simple + } + Inner::RegroleArray => { + &Kind::Array(Type(Inner::Regrole)) + } + Inner::Regcollation => { + &Kind::Simple + } + Inner::RegcollationArray => { + &Kind::Array(Type(Inner::Regcollation)) + } + Inner::Int4multiRange => { + &Kind::Range(Type(Inner::Int4)) + } + Inner::NummultiRange => { + &Kind::Range(Type(Inner::Numeric)) + } + Inner::TsmultiRange => { + &Kind::Range(Type(Inner::Timestamp)) + } + Inner::TstzmultiRange => { + &Kind::Range(Type(Inner::Timestamptz)) + } + Inner::DatemultiRange => { + &Kind::Range(Type(Inner::Date)) + } + Inner::Int8multiRange => { + &Kind::Range(Type(Inner::Int8)) + } + Inner::AnymultiRange => { + &Kind::Pseudo + } + Inner::AnycompatiblemultiRange => { + &Kind::Pseudo + } + Inner::PgBrinBloomSummary => { + &Kind::Simple + } + Inner::PgBrinMinmaxMultiSummary => { + &Kind::Simple + } + Inner::PgMcvList => { + &Kind::Simple + } + Inner::PgSnapshot => { + &Kind::Simple + } + Inner::PgSnapshotArray => { + &Kind::Array(Type(Inner::PgSnapshot)) + } + Inner::Xid8 => { + &Kind::Simple + } + Inner::Anycompatible => { + &Kind::Pseudo + } + Inner::Anycompatiblearray => { + &Kind::Pseudo + } + Inner::Anycompatiblenonarray => { + &Kind::Pseudo + } + Inner::AnycompatibleRange => { + &Kind::Pseudo + } + Inner::Int4multiRangeArray => { + &Kind::Array(Type(Inner::Int4multiRange)) + } + Inner::NummultiRangeArray => { + &Kind::Array(Type(Inner::NummultiRange)) + } + Inner::TsmultiRangeArray => { + &Kind::Array(Type(Inner::TsmultiRange)) + } + Inner::TstzmultiRangeArray => { + &Kind::Array(Type(Inner::TstzmultiRange)) + } + Inner::DatemultiRangeArray => { + &Kind::Array(Type(Inner::DatemultiRange)) + } + Inner::Int8multiRangeArray => { + &Kind::Array(Type(Inner::Int8multiRange)) + } Inner::Other(ref u) => &u.kind, } } @@ -874,6 +1308,16 @@ impl Inner { Inner::RegroleArray => "_regrole", Inner::Regcollation => "regcollation", Inner::RegcollationArray => "_regcollation", + Inner::Int4multiRange => "int4multirange", + Inner::NummultiRange => "nummultirange", + Inner::TsmultiRange => "tsmultirange", + Inner::TstzmultiRange => "tstzmultirange", + Inner::DatemultiRange => "datemultirange", + Inner::Int8multiRange => "int8multirange", + Inner::AnymultiRange => "anymultirange", + Inner::AnycompatiblemultiRange => "anycompatiblemultirange", + Inner::PgBrinBloomSummary => "pg_brin_bloom_summary", + Inner::PgBrinMinmaxMultiSummary => "pg_brin_minmax_multi_summary", Inner::PgMcvList => "pg_mcv_list", Inner::PgSnapshot => "pg_snapshot", Inner::PgSnapshotArray => "_pg_snapshot", @@ -882,6 +1326,12 @@ impl Inner { Inner::Anycompatiblearray => "anycompatiblearray", Inner::Anycompatiblenonarray => "anycompatiblenonarray", Inner::AnycompatibleRange => "anycompatiblerange", + Inner::Int4multiRangeArray => "_int4multirange", + Inner::NummultiRangeArray => "_nummultirange", + Inner::TsmultiRangeArray => "_tsmultirange", + Inner::TstzmultiRangeArray => "_tstzmultirange", + Inner::DatemultiRangeArray => "_datemultirange", + Inner::Int8multiRangeArray => "_int8multirange", Inner::Other(ref u) => &u.name, } } @@ -1370,6 +1820,36 @@ impl Type { /// REGCOLLATION[] pub const REGCOLLATION_ARRAY: Type = Type(Inner::RegcollationArray); + /// INT4MULTIRANGE - multirange of integers + pub const INT4MULTI_RANGE: Type = Type(Inner::Int4multiRange); + + /// NUMMULTIRANGE - multirange of numerics + pub const NUMMULTI_RANGE: Type = Type(Inner::NummultiRange); + + /// TSMULTIRANGE - multirange of timestamps without time zone + pub const TSMULTI_RANGE: Type = Type(Inner::TsmultiRange); + + /// TSTZMULTIRANGE - multirange of timestamps with time zone + pub const TSTZMULTI_RANGE: Type = Type(Inner::TstzmultiRange); + + /// DATEMULTIRANGE - multirange of dates + pub const DATEMULTI_RANGE: Type = Type(Inner::DatemultiRange); + + /// INT8MULTIRANGE - multirange of bigints + pub const INT8MULTI_RANGE: Type = Type(Inner::Int8multiRange); + + /// ANYMULTIRANGE - pseudo-type representing a polymorphic base type that is a multirange + pub const ANYMULTI_RANGE: Type = Type(Inner::AnymultiRange); + + /// ANYCOMPATIBLEMULTIRANGE - pseudo-type representing a multirange over a polymorphic common type + pub const ANYCOMPATIBLEMULTI_RANGE: Type = Type(Inner::AnycompatiblemultiRange); + + /// PG_BRIN_BLOOM_SUMMARY - BRIN bloom summary + pub const PG_BRIN_BLOOM_SUMMARY: Type = Type(Inner::PgBrinBloomSummary); + + /// PG_BRIN_MINMAX_MULTI_SUMMARY - BRIN minmax-multi summary + pub const PG_BRIN_MINMAX_MULTI_SUMMARY: Type = Type(Inner::PgBrinMinmaxMultiSummary); + /// PG_MCV_LIST - multivariate MCV list pub const PG_MCV_LIST: Type = Type(Inner::PgMcvList); @@ -1393,4 +1873,22 @@ impl Type { /// ANYCOMPATIBLERANGE - pseudo-type representing a range over a polymorphic common type pub const ANYCOMPATIBLE_RANGE: Type = Type(Inner::AnycompatibleRange); -} + + /// INT4MULTIRANGE[] + pub const INT4MULTI_RANGE_ARRAY: Type = Type(Inner::Int4multiRangeArray); + + /// NUMMULTIRANGE[] + pub const NUMMULTI_RANGE_ARRAY: Type = Type(Inner::NummultiRangeArray); + + /// TSMULTIRANGE[] + pub const TSMULTI_RANGE_ARRAY: Type = Type(Inner::TsmultiRangeArray); + + /// TSTZMULTIRANGE[] + pub const TSTZMULTI_RANGE_ARRAY: Type = Type(Inner::TstzmultiRangeArray); + + /// DATEMULTIRANGE[] + pub const DATEMULTI_RANGE_ARRAY: Type = Type(Inner::DatemultiRangeArray); + + /// INT8MULTIRANGE[] + pub const INT8MULTI_RANGE_ARRAY: Type = Type(Inner::Int8multiRangeArray); +} \ No newline at end of file diff --git a/tokio-postgres/src/error/sqlstate.rs b/tokio-postgres/src/error/sqlstate.rs index 4eb1ae2d0..6f191fc16 100644 --- a/tokio-postgres/src/error/sqlstate.rs +++ b/tokio-postgres/src/error/sqlstate.rs @@ -114,6 +114,7 @@ impl SqlState { Inner::E2203D => "2203D", Inner::E2203E => "2203E", Inner::E2203F => "2203F", + Inner::E2203G => "2203G", Inner::E23000 => "23000", Inner::E23001 => "23001", Inner::E23502 => "23502", @@ -278,7 +279,7 @@ impl SqlState { Inner::Other(code) => code, } } - + /// 00000 pub const SUCCESSFUL_COMPLETION: SqlState = SqlState(Inner::E00000); @@ -364,8 +365,7 @@ impl SqlState { pub const DIAGNOSTICS_EXCEPTION: SqlState = SqlState(Inner::E0Z000); /// 0Z002 - pub const STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER: SqlState = - SqlState(Inner::E0Z002); + pub const STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER: SqlState = SqlState(Inner::E0Z002); /// 20000 pub const CASE_NOT_FOUND: SqlState = SqlState(Inner::E20000); @@ -580,6 +580,9 @@ impl SqlState { /// 2203F pub const SQL_JSON_SCALAR_REQUIRED: SqlState = SqlState(Inner::E2203F); + /// 2203G + pub const SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE: SqlState = SqlState(Inner::E2203G); + /// 23000 pub const INTEGRITY_CONSTRAINT_VIOLATION: SqlState = SqlState(Inner::E23000); @@ -620,8 +623,7 @@ impl SqlState { pub const INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION: SqlState = SqlState(Inner::E25003); /// 25004 - pub const INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION: SqlState = - SqlState(Inner::E25004); + pub const INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION: SqlState = SqlState(Inner::E25004); /// 25005 pub const NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION: SqlState = SqlState(Inner::E25005); @@ -1178,6 +1180,7 @@ enum Inner { E2203D, E2203E, E2203F, + E2203G, E23000, E23001, E23502, @@ -1341,324 +1344,325 @@ enum Inner { EXX002, Other(Box), } - + #[rustfmt::skip] static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ::phf::Map { key: 12913932095322966823, disps: &[ + (0, 24), (0, 12), - (0, 18), - (0, 25), - (0, 109), - (0, 147), (0, 74), - (0, 0), - (7, 117), - (5, 221), - (0, 26), - (1, 45), - (0, 93), - (0, 25), - (0, 61), - (1, 221), - (10, 17), - (0, 77), - (2, 3), - (0, 216), - (0, 0), - (0, 1), - (1, 168), - (0, 64), - (0, 2), - (0, 7), - (1, 37), - (0, 83), - (3, 24), - (0, 0), (0, 109), - (18, 9), - (1, 230), + (0, 11), + (0, 9), (0, 0), - (0, 4), + (4, 38), + (3, 155), + (0, 6), + (1, 242), + (0, 66), + (0, 53), + (5, 180), + (3, 221), + (7, 230), + (0, 125), + (1, 46), + (0, 11), + (1, 2), + (0, 5), + (0, 13), (0, 171), + (0, 15), + (0, 4), + (0, 22), + (1, 85), + (0, 75), + (2, 0), + (1, 25), + (7, 47), + (0, 45), + (0, 35), + (0, 7), + (7, 124), (0, 0), - (34, 97), - (2, 126), - (44, 49), - (5, 182), - (0, 1), + (14, 104), + (1, 183), + (61, 50), + (3, 76), + (0, 12), + (0, 7), + (4, 189), (0, 1), - (0, 71), - (0, 4), - (5, 164), + (64, 102), (0, 0), - (0, 96), - (13, 58), - (0, 58), - (0, 242), - (0, 72), - (16, 53), + (16, 192), + (24, 19), + (0, 5), + (0, 87), + (0, 89), + (0, 14), ], entries: &[ - ("22034", SqlState::MORE_THAN_ONE_SQL_JSON_ITEM), - ("40P01", SqlState::T_R_DEADLOCK_DETECTED), - ("42703", SqlState::UNDEFINED_COLUMN), - ("42P07", SqlState::DUPLICATE_TABLE), - ("55P04", SqlState::UNSAFE_NEW_ENUM_VALUE_USAGE), + ("2F000", SqlState::SQL_ROUTINE_EXCEPTION), + ("01008", SqlState::WARNING_IMPLICIT_ZERO_BIT_PADDING), + ("42501", SqlState::INSUFFICIENT_PRIVILEGE), + ("22000", SqlState::DATA_EXCEPTION), + ("0100C", SqlState::WARNING_DYNAMIC_RESULT_SETS_RETURNED), + ("2200N", SqlState::INVALID_XML_CONTENT), + ("40001", SqlState::T_R_SERIALIZATION_FAILURE), + ("28P01", SqlState::INVALID_PASSWORD), + ("38000", SqlState::EXTERNAL_ROUTINE_EXCEPTION), ("25006", SqlState::READ_ONLY_SQL_TRANSACTION), - ("2201X", SqlState::INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE), - ("HV021", SqlState::FDW_INCONSISTENT_DESCRIPTOR_INFORMATION), - ("42P02", SqlState::UNDEFINED_PARAMETER), - ("HV00C", SqlState::FDW_INVALID_OPTION_INDEX), - ("08003", SqlState::CONNECTION_DOES_NOT_EXIST), - ("02000", SqlState::NO_DATA), - ("24000", SqlState::INVALID_CURSOR_STATE), - ("2203C", SqlState::SQL_JSON_OBJECT_NOT_FOUND), - ("42601", SqlState::SYNTAX_ERROR), - ("22012", SqlState::DIVISION_BY_ZERO), - ("2203B", SqlState::SQL_JSON_NUMBER_NOT_FOUND), - ("P0003", SqlState::TOO_MANY_ROWS), - ("57P04", SqlState::DATABASE_DROPPED), - ("27000", SqlState::TRIGGERED_DATA_CHANGE_VIOLATION), + ("2203D", SqlState::TOO_MANY_JSON_ARRAY_ELEMENTS), + ("42P09", SqlState::AMBIGUOUS_ALIAS), + ("F0000", SqlState::CONFIG_FILE_ERROR), + ("42P18", SqlState::INDETERMINATE_DATATYPE), + ("40002", SqlState::T_R_INTEGRITY_CONSTRAINT_VIOLATION), + ("22009", SqlState::INVALID_TIME_ZONE_DISPLACEMENT_VALUE), ("42P08", SqlState::AMBIGUOUS_PARAMETER), - ("3F000", SqlState::INVALID_SCHEMA_NAME), - ("42883", SqlState::UNDEFINED_FUNCTION), - ("20000", SqlState::CASE_NOT_FOUND), + ("08000", SqlState::CONNECTION_EXCEPTION), + ("25P01", SqlState::NO_ACTIVE_SQL_TRANSACTION), + ("22024", SqlState::UNTERMINATED_C_STRING), + ("55000", SqlState::OBJECT_NOT_IN_PREREQUISITE_STATE), + ("25001", SqlState::ACTIVE_SQL_TRANSACTION), + ("03000", SqlState::SQL_STATEMENT_NOT_YET_COMPLETE), + ("42710", SqlState::DUPLICATE_OBJECT), + ("2D000", SqlState::INVALID_TRANSACTION_TERMINATION), ("2200G", SqlState::MOST_SPECIFIC_TYPE_MISMATCH), - ("42939", SqlState::RESERVED_NAME), - ("42602", SqlState::INVALID_NAME), - ("HV004", SqlState::FDW_INVALID_DATA_TYPE), - ("HV007", SqlState::FDW_INVALID_COLUMN_NAME), + ("22022", SqlState::INDICATOR_OVERFLOW), + ("55006", SqlState::OBJECT_IN_USE), + ("53200", SqlState::OUT_OF_MEMORY), + ("22012", SqlState::DIVISION_BY_ZERO), + ("P0002", SqlState::NO_DATA_FOUND), + ("XX001", SqlState::DATA_CORRUPTED), + ("22P05", SqlState::UNTRANSLATABLE_CHARACTER), + ("40003", SqlState::T_R_STATEMENT_COMPLETION_UNKNOWN), + ("22021", SqlState::CHARACTER_NOT_IN_REPERTOIRE), + ("25000", SqlState::INVALID_TRANSACTION_STATE), + ("42P15", SqlState::INVALID_SCHEMA_DEFINITION), + ("0B000", SqlState::INVALID_TRANSACTION_INITIATION), + ("22004", SqlState::NULL_VALUE_NOT_ALLOWED), + ("42804", SqlState::DATATYPE_MISMATCH), + ("42803", SqlState::GROUPING_ERROR), + ("02001", SqlState::NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED), + ("25002", SqlState::BRANCH_TRANSACTION_ALREADY_ACTIVE), + ("28000", SqlState::INVALID_AUTHORIZATION_SPECIFICATION), + ("HV009", SqlState::FDW_INVALID_USE_OF_NULL_POINTER), + ("22P01", SqlState::FLOATING_POINT_EXCEPTION), + ("2B000", SqlState::DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST), + ("42723", SqlState::DUPLICATE_FUNCTION), + ("21000", SqlState::CARDINALITY_VIOLATION), + ("0Z002", SqlState::STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER), + ("23505", SqlState::UNIQUE_VIOLATION), + ("HV00J", SqlState::FDW_OPTION_NAME_NOT_FOUND), + ("23P01", SqlState::EXCLUSION_VIOLATION), + ("39P03", SqlState::E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED), + ("42P10", SqlState::INVALID_COLUMN_REFERENCE), + ("2202H", SqlState::INVALID_TABLESAMPLE_ARGUMENT), + ("55P04", SqlState::UNSAFE_NEW_ENUM_VALUE_USAGE), + ("P0000", SqlState::PLPGSQL_ERROR), ("2F005", SqlState::S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), - ("22030", SqlState::DUPLICATE_JSON_OBJECT_KEY_VALUE), - ("53100", SqlState::DISK_FULL), + ("HV00M", SqlState::FDW_UNABLE_TO_CREATE_REPLY), + ("0A000", SqlState::FEATURE_NOT_SUPPORTED), + ("24000", SqlState::INVALID_CURSOR_STATE), + ("25008", SqlState::HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL), + ("01003", SqlState::WARNING_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION), + ("42712", SqlState::DUPLICATE_ALIAS), + ("HV014", SqlState::FDW_TOO_MANY_HANDLES), + ("58030", SqlState::IO_ERROR), + ("2201W", SqlState::INVALID_ROW_COUNT_IN_LIMIT_CLAUSE), + ("22033", SqlState::INVALID_SQL_JSON_SUBSCRIPT), + ("2BP01", SqlState::DEPENDENT_OBJECTS_STILL_EXIST), ("HV005", SqlState::FDW_COLUMN_NAME_NOT_FOUND), + ("25004", SqlState::INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION), + ("54000", SqlState::PROGRAM_LIMIT_EXCEEDED), + ("20000", SqlState::CASE_NOT_FOUND), + ("2203G", SqlState::SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE), + ("22038", SqlState::SINGLETON_SQL_JSON_ITEM_REQUIRED), + ("22007", SqlState::INVALID_DATETIME_FORMAT), + ("08004", SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION), ("2200H", SqlState::SEQUENCE_GENERATOR_LIMIT_EXCEEDED), - ("2201W", SqlState::INVALID_ROW_COUNT_IN_LIMIT_CLAUSE), - ("42712", SqlState::DUPLICATE_ALIAS), - ("42622", SqlState::NAME_TOO_LONG), - ("22035", SqlState::NO_SQL_JSON_ITEM), - ("42P18", SqlState::INDETERMINATE_DATATYPE), - ("39P01", SqlState::E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), - ("01000", SqlState::WARNING), - ("2F004", SqlState::S_R_E_READING_SQL_DATA_NOT_PERMITTED), - ("22023", SqlState::INVALID_PARAMETER_VALUE), - ("2200T", SqlState::INVALID_XML_PROCESSING_INSTRUCTION), - ("22013", SqlState::INVALID_PRECEDING_OR_FOLLOWING_SIZE), - ("57P01", SqlState::ADMIN_SHUTDOWN), - ("2202E", SqlState::ARRAY_ELEMENT_ERROR), + ("HV00D", SqlState::FDW_INVALID_OPTION_NAME), + ("P0004", SqlState::ASSERT_FAILURE), ("22018", SqlState::INVALID_CHARACTER_VALUE_FOR_CAST), - ("0F000", SqlState::LOCATOR_EXCEPTION), - ("2D000", SqlState::INVALID_TRANSACTION_TERMINATION), - ("HV009", SqlState::FDW_INVALID_USE_OF_NULL_POINTER), - ("57000", SqlState::OPERATOR_INTERVENTION), - ("25002", SqlState::BRANCH_TRANSACTION_ALREADY_ACTIVE), - ("25004", SqlState::INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION), - ("22009", SqlState::INVALID_TIME_ZONE_DISPLACEMENT_VALUE), - ("HV090", SqlState::FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH), - ("42725", SqlState::AMBIGUOUS_FUNCTION), - ("2F003", SqlState::S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), - ("44000", SqlState::WITH_CHECK_OPTION_VIOLATION), - ("22032", SqlState::INVALID_JSON_TEXT), - ("22036", SqlState::NON_NUMERIC_SQL_JSON_ITEM), - ("2201E", SqlState::INVALID_ARGUMENT_FOR_LOG), - ("25P02", SqlState::IN_FAILED_SQL_TRANSACTION), - ("22001", SqlState::STRING_DATA_RIGHT_TRUNCATION), - ("2201F", SqlState::INVALID_ARGUMENT_FOR_POWER_FUNCTION), - ("01006", SqlState::WARNING_PRIVILEGE_NOT_REVOKED), - ("428C9", SqlState::GENERATED_ALWAYS), - ("22003", SqlState::NUMERIC_VALUE_OUT_OF_RANGE), - ("22P01", SqlState::FLOATING_POINT_EXCEPTION), - ("HV00M", SqlState::FDW_UNABLE_TO_CREATE_REPLY), - ("2201G", SqlState::INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), - ("34000", SqlState::INVALID_CURSOR_NAME), - ("42846", SqlState::CANNOT_COERCE), - ("2201B", SqlState::INVALID_REGULAR_EXPRESSION), - ("2202G", SqlState::INVALID_TABLESAMPLE_REPEAT), - ("42704", SqlState::UNDEFINED_OBJECT), - ("72000", SqlState::SNAPSHOT_TOO_OLD), - ("53400", SqlState::CONFIGURATION_LIMIT_EXCEEDED), - ("HV00L", SqlState::FDW_UNABLE_TO_CREATE_EXECUTION), - ("2B000", SqlState::DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST), - ("22010", SqlState::INVALID_INDICATOR_PARAMETER_VALUE), + ("0L000", SqlState::INVALID_GRANTOR), + ("22P04", SqlState::BAD_COPY_FILE_FORMAT), + ("22031", SqlState::INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION), + ("01P01", SqlState::WARNING_DEPRECATED_FEATURE), + ("0LP01", SqlState::INVALID_GRANT_OPERATION), + ("58P02", SqlState::DUPLICATE_FILE), + ("26000", SqlState::INVALID_SQL_STATEMENT_NAME), ("54001", SqlState::STATEMENT_TOO_COMPLEX), - ("53200", SqlState::OUT_OF_MEMORY), - ("38001", SqlState::E_R_E_CONTAINING_SQL_NOT_PERMITTED), - ("22022", SqlState::INDICATOR_OVERFLOW), - ("2203E", SqlState::TOO_MANY_JSON_OBJECT_MEMBERS), - ("XX000", SqlState::INTERNAL_ERROR), - ("22025", SqlState::INVALID_ESCAPE_SEQUENCE), - ("09000", SqlState::TRIGGERED_ACTION_EXCEPTION), - ("HV008", SqlState::FDW_INVALID_COLUMN_NUMBER), - ("25P01", SqlState::NO_ACTIVE_SQL_TRANSACTION), - ("23505", SqlState::UNIQUE_VIOLATION), - ("3B000", SqlState::SAVEPOINT_EXCEPTION), - ("F0000", SqlState::CONFIG_FILE_ERROR), + ("22010", SqlState::INVALID_INDICATOR_PARAMETER_VALUE), + ("HV00C", SqlState::FDW_INVALID_OPTION_INDEX), + ("22008", SqlState::DATETIME_FIELD_OVERFLOW), + ("42P06", SqlState::DUPLICATE_SCHEMA), + ("25007", SqlState::SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED), + ("42P20", SqlState::WINDOWING_ERROR), + ("HV091", SqlState::FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER), + ("HV021", SqlState::FDW_INCONSISTENT_DESCRIPTOR_INFORMATION), + ("42702", SqlState::AMBIGUOUS_COLUMN), + ("02000", SqlState::NO_DATA), ("54011", SqlState::TOO_MANY_COLUMNS), - ("XX002", SqlState::INDEX_CORRUPTED), - ("2203F", SqlState::SQL_JSON_SCALAR_REQUIRED), + ("HV004", SqlState::FDW_INVALID_DATA_TYPE), + ("01006", SqlState::WARNING_PRIVILEGE_NOT_REVOKED), + ("42701", SqlState::DUPLICATE_COLUMN), + ("08P01", SqlState::PROTOCOL_VIOLATION), + ("42622", SqlState::NAME_TOO_LONG), + ("P0003", SqlState::TOO_MANY_ROWS), + ("22003", SqlState::NUMERIC_VALUE_OUT_OF_RANGE), + ("42P03", SqlState::DUPLICATE_CURSOR), + ("23001", SqlState::RESTRICT_VIOLATION), + ("57000", SqlState::OPERATOR_INTERVENTION), + ("22027", SqlState::TRIM_ERROR), ("42P12", SqlState::INVALID_DATABASE_DEFINITION), + ("3B000", SqlState::SAVEPOINT_EXCEPTION), + ("2201B", SqlState::INVALID_REGULAR_EXPRESSION), + ("22030", SqlState::DUPLICATE_JSON_OBJECT_KEY_VALUE), + ("2F004", SqlState::S_R_E_READING_SQL_DATA_NOT_PERMITTED), + ("428C9", SqlState::GENERATED_ALWAYS), + ("2200S", SqlState::INVALID_XML_COMMENT), + ("22039", SqlState::SQL_JSON_ARRAY_NOT_FOUND), + ("42809", SqlState::WRONG_OBJECT_TYPE), + ("2201X", SqlState::INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE), + ("39001", SqlState::E_R_I_E_INVALID_SQLSTATE_RETURNED), + ("25P02", SqlState::IN_FAILED_SQL_TRANSACTION), + ("0P000", SqlState::INVALID_ROLE_SPECIFICATION), + ("HV00N", SqlState::FDW_UNABLE_TO_ESTABLISH_CONNECTION), + ("53100", SqlState::DISK_FULL), + ("42601", SqlState::SYNTAX_ERROR), + ("23000", SqlState::INTEGRITY_CONSTRAINT_VIOLATION), + ("HV006", SqlState::FDW_INVALID_DATA_TYPE_DESCRIPTORS), ("HV00B", SqlState::FDW_INVALID_HANDLE), - ("55006", SqlState::OBJECT_IN_USE), - ("42P01", SqlState::UNDEFINED_TABLE), - ("25P03", SqlState::IDLE_IN_TRANSACTION_SESSION_TIMEOUT), - ("57P05", SqlState::IDLE_SESSION_TIMEOUT), + ("HV00Q", SqlState::FDW_SCHEMA_NOT_FOUND), + ("01000", SqlState::WARNING), + ("42883", SqlState::UNDEFINED_FUNCTION), + ("57P01", SqlState::ADMIN_SHUTDOWN), ("22037", SqlState::NON_UNIQUE_KEYS_IN_A_JSON_OBJECT), - ("2203A", SqlState::SQL_JSON_MEMBER_NOT_FOUND), - ("P0004", SqlState::ASSERT_FAILURE), - ("58000", SqlState::SYSTEM_ERROR), - ("42P21", SqlState::COLLATION_MISMATCH), - ("57P02", SqlState::CRASH_SHUTDOWN), - ("42830", SqlState::INVALID_FOREIGN_KEY), - ("0LP01", SqlState::INVALID_GRANT_OPERATION), - ("22P02", SqlState::INVALID_TEXT_REPRESENTATION), - ("22039", SqlState::SQL_JSON_ARRAY_NOT_FOUND), - ("28P01", SqlState::INVALID_PASSWORD), - ("22011", SqlState::SUBSTRING_ERROR), - ("HV00J", SqlState::FDW_OPTION_NAME_NOT_FOUND), - ("2200C", SqlState::INVALID_USE_OF_ESCAPE_CHARACTER), - ("08006", SqlState::CONNECTION_FAILURE), - ("22021", SqlState::CHARACTER_NOT_IN_REPERTOIRE), - ("21000", SqlState::CARDINALITY_VIOLATION), - ("42803", SqlState::GROUPING_ERROR), ("00000", SqlState::SUCCESSFUL_COMPLETION), + ("55P03", SqlState::LOCK_NOT_AVAILABLE), + ("42P01", SqlState::UNDEFINED_TABLE), + ("42830", SqlState::INVALID_FOREIGN_KEY), + ("22005", SqlState::ERROR_IN_ASSIGNMENT), + ("22025", SqlState::INVALID_ESCAPE_SEQUENCE), + ("XX002", SqlState::INDEX_CORRUPTED), ("42P16", SqlState::INVALID_TABLE_DEFINITION), - ("38002", SqlState::E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED), - ("57P03", SqlState::CANNOT_CONNECT_NOW), + ("55P02", SqlState::CANT_CHANGE_RUNTIME_PARAM), + ("22019", SqlState::INVALID_ESCAPE_CHARACTER), + ("P0001", SqlState::RAISE_EXCEPTION), + ("72000", SqlState::SNAPSHOT_TOO_OLD), + ("42P11", SqlState::INVALID_CURSOR_DEFINITION), + ("40P01", SqlState::T_R_DEADLOCK_DETECTED), + ("57P02", SqlState::CRASH_SHUTDOWN), + ("HV00A", SqlState::FDW_INVALID_STRING_FORMAT), + ("2F002", SqlState::S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED), + ("23503", SqlState::FOREIGN_KEY_VIOLATION), + ("40000", SqlState::TRANSACTION_ROLLBACK), + ("22032", SqlState::INVALID_JSON_TEXT), + ("2202E", SqlState::ARRAY_ELEMENT_ERROR), + ("42P19", SqlState::INVALID_RECURSION), + ("42611", SqlState::INVALID_COLUMN_DEFINITION), + ("42P13", SqlState::INVALID_FUNCTION_DEFINITION), + ("25003", SqlState::INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION), + ("39P02", SqlState::E_R_I_E_SRF_PROTOCOL_VIOLATED), + ("XX000", SqlState::INTERNAL_ERROR), + ("08006", SqlState::CONNECTION_FAILURE), + ("57P04", SqlState::DATABASE_DROPPED), + ("42P07", SqlState::DUPLICATE_TABLE), + ("22P03", SqlState::INVALID_BINARY_REPRESENTATION), + ("22035", SqlState::NO_SQL_JSON_ITEM), + ("42P14", SqlState::INVALID_PSTATEMENT_DEFINITION), + ("01007", SqlState::WARNING_PRIVILEGE_NOT_GRANTED), + ("38004", SqlState::E_R_E_READING_SQL_DATA_NOT_PERMITTED), + ("42P21", SqlState::COLLATION_MISMATCH), + ("0Z000", SqlState::DIAGNOSTICS_EXCEPTION), + ("HV001", SqlState::FDW_OUT_OF_MEMORY), + ("0F000", SqlState::LOCATOR_EXCEPTION), + ("22013", SqlState::INVALID_PRECEDING_OR_FOLLOWING_SIZE), + ("2201E", SqlState::INVALID_ARGUMENT_FOR_LOG), + ("22011", SqlState::SUBSTRING_ERROR), + ("42602", SqlState::INVALID_NAME), ("01004", SqlState::WARNING_STRING_DATA_RIGHT_TRUNCATION), - ("HV00K", SqlState::FDW_REPLY_HANDLE), - ("42P06", SqlState::DUPLICATE_SCHEMA), - ("54000", SqlState::PROGRAM_LIMIT_EXCEEDED), - ("2200S", SqlState::INVALID_XML_COMMENT), - ("42000", SqlState::SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION), - ("42P03", SqlState::DUPLICATE_CURSOR), + ("42P02", SqlState::UNDEFINED_PARAMETER), + ("2203C", SqlState::SQL_JSON_OBJECT_NOT_FOUND), ("HV002", SqlState::FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), - ("2202H", SqlState::INVALID_TABLESAMPLE_ARGUMENT), - ("08001", SqlState::SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), - ("0L000", SqlState::INVALID_GRANTOR), - ("2200L", SqlState::NOT_AN_XML_DOCUMENT), - ("HV006", SqlState::FDW_INVALID_DATA_TYPE_DESCRIPTORS), - ("55000", SqlState::OBJECT_NOT_IN_PREREQUISITE_STATE), + ("0F001", SqlState::L_E_INVALID_SPECIFICATION), ("58P01", SqlState::UNDEFINED_FILE), - ("0B000", SqlState::INVALID_TRANSACTION_INITIATION), - ("22000", SqlState::DATA_EXCEPTION), - ("HV00R", SqlState::FDW_TABLE_NOT_FOUND), - ("2F002", SqlState::S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED), - ("01007", SqlState::WARNING_PRIVILEGE_NOT_GRANTED), - ("42P19", SqlState::INVALID_RECURSION), + ("38001", SqlState::E_R_E_CONTAINING_SQL_NOT_PERMITTED), + ("42703", SqlState::UNDEFINED_COLUMN), + ("57P05", SqlState::IDLE_SESSION_TIMEOUT), + ("57P03", SqlState::CANNOT_CONNECT_NOW), + ("HV007", SqlState::FDW_INVALID_COLUMN_NAME), + ("22014", SqlState::INVALID_ARGUMENT_FOR_NTILE), + ("22P06", SqlState::NONSTANDARD_USE_OF_ESCAPE_CHARACTER), + ("2203F", SqlState::SQL_JSON_SCALAR_REQUIRED), + ("2200F", SqlState::ZERO_LENGTH_CHARACTER_STRING), + ("09000", SqlState::TRIGGERED_ACTION_EXCEPTION), + ("2201F", SqlState::INVALID_ARGUMENT_FOR_POWER_FUNCTION), + ("08003", SqlState::CONNECTION_DOES_NOT_EXIST), + ("38002", SqlState::E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED), + ("F0001", SqlState::LOCK_FILE_EXISTS), + ("42P22", SqlState::INDETERMINATE_COLLATION), + ("2200C", SqlState::INVALID_USE_OF_ESCAPE_CHARACTER), + ("2203E", SqlState::TOO_MANY_JSON_OBJECT_MEMBERS), + ("23514", SqlState::CHECK_VIOLATION), + ("22P02", SqlState::INVALID_TEXT_REPRESENTATION), + ("54023", SqlState::TOO_MANY_ARGUMENTS), + ("2200T", SqlState::INVALID_XML_PROCESSING_INSTRUCTION), ("22016", SqlState::INVALID_ARGUMENT_FOR_NTH_VALUE), - ("42702", SqlState::AMBIGUOUS_COLUMN), - ("25005", SqlState::NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION), - ("22004", SqlState::NULL_VALUE_NOT_ALLOWED), - ("42P05", SqlState::DUPLICATE_PSTATEMENT), - ("39001", SqlState::E_R_I_E_INVALID_SQLSTATE_RETURNED), - ("22038", SqlState::SINGLETON_SQL_JSON_ITEM_REQUIRED), - ("22008", SqlState::DATETIME_FIELD_OVERFLOW), - ("38003", SqlState::E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), - ("53000", SqlState::INSUFFICIENT_RESOURCES), + ("25P03", SqlState::IDLE_IN_TRANSACTION_SESSION_TIMEOUT), ("3B001", SqlState::S_E_INVALID_SPECIFICATION), - ("28000", SqlState::INVALID_AUTHORIZATION_SPECIFICATION), - ("P0000", SqlState::PLPGSQL_ERROR), - ("38000", SqlState::EXTERNAL_ROUTINE_EXCEPTION), - ("22019", SqlState::INVALID_ESCAPE_CHARACTER), - ("22015", SqlState::INTERVAL_FIELD_OVERFLOW), - ("42710", SqlState::DUPLICATE_OBJECT), - ("2200M", SqlState::INVALID_XML_DOCUMENT), + ("08001", SqlState::SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), + ("22036", SqlState::NON_NUMERIC_SQL_JSON_ITEM), + ("3F000", SqlState::INVALID_SCHEMA_NAME), + ("39P01", SqlState::E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + ("22026", SqlState::STRING_DATA_LENGTH_MISMATCH), + ("42P17", SqlState::INVALID_OBJECT_DEFINITION), + ("22034", SqlState::MORE_THAN_ONE_SQL_JSON_ITEM), ("HV000", SqlState::FDW_ERROR), - ("22P05", SqlState::UNTRANSLATABLE_CHARACTER), - ("0100C", SqlState::WARNING_DYNAMIC_RESULT_SETS_RETURNED), - ("55P02", SqlState::CANT_CHANGE_RUNTIME_PARAM), - ("01003", SqlState::WARNING_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION), - ("2200N", SqlState::INVALID_XML_CONTENT), - ("2F000", SqlState::SQL_ROUTINE_EXCEPTION), - ("08007", SqlState::TRANSACTION_RESOLUTION_UNKNOWN), ("2200B", SqlState::ESCAPE_CHARACTER_CONFLICT), - ("22P03", SqlState::INVALID_BINARY_REPRESENTATION), - ("42P09", SqlState::AMBIGUOUS_ALIAS), + ("HV008", SqlState::FDW_INVALID_COLUMN_NUMBER), + ("34000", SqlState::INVALID_CURSOR_NAME), + ("2201G", SqlState::INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION), + ("44000", SqlState::WITH_CHECK_OPTION_VIOLATION), + ("HV010", SqlState::FDW_FUNCTION_SEQUENCE_ERROR), ("39004", SqlState::E_R_I_E_NULL_VALUE_NOT_ALLOWED), - ("23502", SqlState::NOT_NULL_VIOLATION), - ("2203D", SqlState::TOO_MANY_JSON_ARRAY_ELEMENTS), - ("42P15", SqlState::INVALID_SCHEMA_DEFINITION), - ("08004", SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION), - ("HV00N", SqlState::FDW_UNABLE_TO_ESTABLISH_CONNECTION), - ("0A000", SqlState::FEATURE_NOT_SUPPORTED), - ("57014", SqlState::QUERY_CANCELED), - ("22033", SqlState::INVALID_SQL_JSON_SUBSCRIPT), - ("0F001", SqlState::L_E_INVALID_SPECIFICATION), - ("HV00A", SqlState::FDW_INVALID_STRING_FORMAT), - ("39P02", SqlState::E_R_I_E_SRF_PROTOCOL_VIOLATED), - ("42701", SqlState::DUPLICATE_COLUMN), - ("42611", SqlState::INVALID_COLUMN_DEFINITION), - ("HV001", SqlState::FDW_OUT_OF_MEMORY), - ("HV091", SqlState::FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER), - ("23P01", SqlState::EXCLUSION_VIOLATION), - ("F0001", SqlState::LOCK_FILE_EXISTS), - ("42501", SqlState::INSUFFICIENT_PRIVILEGE), - ("22026", SqlState::STRING_DATA_LENGTH_MISMATCH), - ("54023", SqlState::TOO_MANY_ARGUMENTS), - ("01008", SqlState::WARNING_IMPLICIT_ZERO_BIT_PADDING), - ("42P04", SqlState::DUPLICATE_DATABASE), - ("22027", SqlState::TRIM_ERROR), - ("53300", SqlState::TOO_MANY_CONNECTIONS), - ("0Z002", SqlState::STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER), - ("42P14", SqlState::INVALID_PSTATEMENT_DEFINITION), - ("P0001", SqlState::RAISE_EXCEPTION), - ("HV014", SqlState::FDW_TOO_MANY_HANDLES), - ("40002", SqlState::T_R_INTEGRITY_CONSTRAINT_VIOLATION), + ("22001", SqlState::STRING_DATA_RIGHT_TRUNCATION), ("3D000", SqlState::INVALID_CATALOG_NAME), - ("03000", SqlState::SQL_STATEMENT_NOT_YET_COMPLETE), - ("22024", SqlState::UNTERMINATED_C_STRING), - ("42P13", SqlState::INVALID_FUNCTION_DEFINITION), - ("08000", SqlState::CONNECTION_EXCEPTION), - ("25007", SqlState::SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED), - ("40001", SqlState::T_R_SERIALIZATION_FAILURE), - ("25001", SqlState::ACTIVE_SQL_TRANSACTION), - ("HV00Q", SqlState::FDW_SCHEMA_NOT_FOUND), - ("22P04", SqlState::BAD_COPY_FILE_FORMAT), - ("XX001", SqlState::DATA_CORRUPTED), - ("23503", SqlState::FOREIGN_KEY_VIOLATION), - ("23514", SqlState::CHECK_VIOLATION), - ("42809", SqlState::WRONG_OBJECT_TYPE), - ("2200F", SqlState::ZERO_LENGTH_CHARACTER_STRING), - ("2BP01", SqlState::DEPENDENT_OBJECTS_STILL_EXIST), - ("25008", SqlState::HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL), - ("55P03", SqlState::LOCK_NOT_AVAILABLE), - ("42P22", SqlState::INDETERMINATE_COLLATION), - ("HV00D", SqlState::FDW_INVALID_OPTION_NAME), - ("42P17", SqlState::INVALID_OBJECT_DEFINITION), - ("23001", SqlState::RESTRICT_VIOLATION), - ("22P06", SqlState::NONSTANDARD_USE_OF_ESCAPE_CHARACTER), - ("22031", SqlState::INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION), - ("2200D", SqlState::INVALID_ESCAPE_OCTET), - ("0Z000", SqlState::DIAGNOSTICS_EXCEPTION), - ("HV024", SqlState::FDW_INVALID_ATTRIBUTE_VALUE), - ("22005", SqlState::ERROR_IN_ASSIGNMENT), - ("58P02", SqlState::DUPLICATE_FILE), + ("25005", SqlState::NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION), + ("2200L", SqlState::NOT_AN_XML_DOCUMENT), + ("27000", SqlState::TRIGGERED_DATA_CHANGE_VIOLATION), + ("HV090", SqlState::FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH), + ("42939", SqlState::RESERVED_NAME), + ("58000", SqlState::SYSTEM_ERROR), + ("2200M", SqlState::INVALID_XML_DOCUMENT), + ("HV00L", SqlState::FDW_UNABLE_TO_CREATE_EXECUTION), + ("57014", SqlState::QUERY_CANCELED), + ("23502", SqlState::NOT_NULL_VIOLATION), + ("22002", SqlState::NULL_VALUE_NO_INDICATOR_PARAMETER), + ("HV00R", SqlState::FDW_TABLE_NOT_FOUND), ("HV00P", SqlState::FDW_NO_SCHEMAS), - ("42P10", SqlState::INVALID_COLUMN_REFERENCE), - ("42P20", SqlState::WINDOWING_ERROR), - ("25000", SqlState::INVALID_TRANSACTION_STATE), - ("38004", SqlState::E_R_E_READING_SQL_DATA_NOT_PERMITTED), - ("01P01", SqlState::WARNING_DEPRECATED_FEATURE), - ("40000", SqlState::TRANSACTION_ROLLBACK), - ("58030", SqlState::IO_ERROR), - ("26000", SqlState::INVALID_SQL_STATEMENT_NAME), - ("22007", SqlState::INVALID_DATETIME_FORMAT), - ("23000", SqlState::INTEGRITY_CONSTRAINT_VIOLATION), - ("0P000", SqlState::INVALID_ROLE_SPECIFICATION), - ("22014", SqlState::INVALID_ARGUMENT_FOR_NTILE), - ("P0002", SqlState::NO_DATA_FOUND), - ("39P03", SqlState::E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED), + ("38003", SqlState::E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), ("39000", SqlState::EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - ("42P11", SqlState::INVALID_CURSOR_DEFINITION), - ("HV010", SqlState::FDW_FUNCTION_SEQUENCE_ERROR), - ("22002", SqlState::NULL_VALUE_NO_INDICATOR_PARAMETER), - ("08P01", SqlState::PROTOCOL_VIOLATION), - ("42723", SqlState::DUPLICATE_FUNCTION), - ("40003", SqlState::T_R_STATEMENT_COMPLETION_UNKNOWN), - ("25003", SqlState::INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION), - ("02001", SqlState::NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED), - ("42804", SqlState::DATATYPE_MISMATCH), + ("22015", SqlState::INTERVAL_FIELD_OVERFLOW), + ("HV00K", SqlState::FDW_REPLY_HANDLE), + ("HV024", SqlState::FDW_INVALID_ATTRIBUTE_VALUE), + ("2200D", SqlState::INVALID_ESCAPE_OCTET), + ("08007", SqlState::TRANSACTION_RESOLUTION_UNKNOWN), + ("2F003", SqlState::S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), + ("42725", SqlState::AMBIGUOUS_FUNCTION), + ("2203A", SqlState::SQL_JSON_MEMBER_NOT_FOUND), + ("42846", SqlState::CANNOT_COERCE), + ("42P04", SqlState::DUPLICATE_DATABASE), + ("42000", SqlState::SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION), + ("2203B", SqlState::SQL_JSON_NUMBER_NOT_FOUND), + ("42P05", SqlState::DUPLICATE_PSTATEMENT), + ("53300", SqlState::TOO_MANY_CONNECTIONS), + ("53400", SqlState::CONFIGURATION_LIMIT_EXCEEDED), + ("42704", SqlState::UNDEFINED_OBJECT), + ("2202G", SqlState::INVALID_TABLESAMPLE_REPEAT), + ("22023", SqlState::INVALID_PARAMETER_VALUE), + ("53000", SqlState::INSUFFICIENT_RESOURCES), ], }; From 0c86f9362fe98a584fb80828650b22417bb661a2 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Mon, 21 Nov 2022 16:52:58 -0500 Subject: [PATCH 015/207] Add multirange kind and run cargo fmt --- codegen/src/type_gen.rs | 24 +- postgres-types/src/lib.rs | 2 + postgres-types/src/type_gen.rs | 744 +++++++-------------------- tokio-postgres/src/error/sqlstate.rs | 10 +- 4 files changed, 216 insertions(+), 564 deletions(-) diff --git a/codegen/src/type_gen.rs b/codegen/src/type_gen.rs index 249c5530a..fd7a56450 100644 --- a/codegen/src/type_gen.rs +++ b/codegen/src/type_gen.rs @@ -17,6 +17,7 @@ struct Type { variant: String, ident: String, kind: String, + typtype: Option, element: u32, doc: String, } @@ -217,12 +218,18 @@ fn parse_types() -> BTreeMap { continue; } + let typtype = raw_type.get("typtype").cloned(); + let element = match &*kind { - "R" => match &*raw_type["typtype"] { + "R" => match typtype + .as_ref() + .expect("range type must have typtype") + .as_str() + { "r" => range_elements[&oid], "m" => multi_range_elements[&oid], typtype => panic!("invalid range typtype {}", typtype), - } + }, "A" => oids_by_name[&raw_type["typelem"]], _ => 0, }; @@ -248,6 +255,7 @@ fn parse_types() -> BTreeMap { variant, ident, kind: "A".to_string(), + typtype: None, element: oid, doc, }; @@ -259,6 +267,7 @@ fn parse_types() -> BTreeMap { variant, ident, kind, + typtype, element, doc, }; @@ -362,7 +371,16 @@ fn make_impl(w: &mut BufWriter, types: &BTreeMap) { let kind = match &*type_.kind { "P" => "Pseudo".to_owned(), "A" => format!("Array(Type(Inner::{}))", types[&type_.element].variant), - "R" => format!("Range(Type(Inner::{}))", types[&type_.element].variant), + "R" => match type_ + .typtype + .as_ref() + .expect("range type must have typtype") + .as_str() + { + "r" => format!("Range(Type(Inner::{}))", types[&type_.element].variant), + "m" => format!("Multirange(Type(Inner::{}))", types[&type_.element].variant), + typtype => panic!("invalid range typtype {}", typtype), + }, _ => "Simple".to_owned(), }; diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index ca4233f8a..fa49d99eb 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -320,6 +320,8 @@ pub enum Kind { Array(Type), /// A range type along with the type of its elements. Range(Type), + /// A multirange type along with the type of its elements. + Multirange(Type), /// A domain type along with its underlying type. Domain(Type), /// A composite type along with information about its fields. diff --git a/postgres-types/src/type_gen.rs b/postgres-types/src/type_gen.rs index 901fb46e0..a1bc3f85c 100644 --- a/postgres-types/src/type_gen.rs +++ b/postgres-types/src/type_gen.rs @@ -1,7 +1,7 @@ // Autogenerated file - DO NOT EDIT use std::sync::Arc; -use crate::{Type, Oid, Kind}; +use crate::{Kind, Oid, Type}; #[derive(PartialEq, Eq, Debug, Hash)] pub struct Other { @@ -586,561 +586,191 @@ impl Inner { pub fn kind(&self) -> &Kind { match *self { - Inner::Bool => { - &Kind::Simple - } - Inner::Bytea => { - &Kind::Simple - } - Inner::Char => { - &Kind::Simple - } - Inner::Name => { - &Kind::Simple - } - Inner::Int8 => { - &Kind::Simple - } - Inner::Int2 => { - &Kind::Simple - } - Inner::Int2Vector => { - &Kind::Array(Type(Inner::Int2)) - } - Inner::Int4 => { - &Kind::Simple - } - Inner::Regproc => { - &Kind::Simple - } - Inner::Text => { - &Kind::Simple - } - Inner::Oid => { - &Kind::Simple - } - Inner::Tid => { - &Kind::Simple - } - Inner::Xid => { - &Kind::Simple - } - Inner::Cid => { - &Kind::Simple - } - Inner::OidVector => { - &Kind::Array(Type(Inner::Oid)) - } - Inner::PgDdlCommand => { - &Kind::Pseudo - } - Inner::Json => { - &Kind::Simple - } - Inner::Xml => { - &Kind::Simple - } - Inner::XmlArray => { - &Kind::Array(Type(Inner::Xml)) - } - Inner::PgNodeTree => { - &Kind::Simple - } - Inner::JsonArray => { - &Kind::Array(Type(Inner::Json)) - } - Inner::TableAmHandler => { - &Kind::Pseudo - } - Inner::Xid8Array => { - &Kind::Array(Type(Inner::Xid8)) - } - Inner::IndexAmHandler => { - &Kind::Pseudo - } - Inner::Point => { - &Kind::Simple - } - Inner::Lseg => { - &Kind::Simple - } - Inner::Path => { - &Kind::Simple - } - Inner::Box => { - &Kind::Simple - } - Inner::Polygon => { - &Kind::Simple - } - Inner::Line => { - &Kind::Simple - } - Inner::LineArray => { - &Kind::Array(Type(Inner::Line)) - } - Inner::Cidr => { - &Kind::Simple - } - Inner::CidrArray => { - &Kind::Array(Type(Inner::Cidr)) - } - Inner::Float4 => { - &Kind::Simple - } - Inner::Float8 => { - &Kind::Simple - } - Inner::Unknown => { - &Kind::Simple - } - Inner::Circle => { - &Kind::Simple - } - Inner::CircleArray => { - &Kind::Array(Type(Inner::Circle)) - } - Inner::Macaddr8 => { - &Kind::Simple - } - Inner::Macaddr8Array => { - &Kind::Array(Type(Inner::Macaddr8)) - } - Inner::Money => { - &Kind::Simple - } - Inner::MoneyArray => { - &Kind::Array(Type(Inner::Money)) - } - Inner::Macaddr => { - &Kind::Simple - } - Inner::Inet => { - &Kind::Simple - } - Inner::BoolArray => { - &Kind::Array(Type(Inner::Bool)) - } - Inner::ByteaArray => { - &Kind::Array(Type(Inner::Bytea)) - } - Inner::CharArray => { - &Kind::Array(Type(Inner::Char)) - } - Inner::NameArray => { - &Kind::Array(Type(Inner::Name)) - } - Inner::Int2Array => { - &Kind::Array(Type(Inner::Int2)) - } - Inner::Int2VectorArray => { - &Kind::Array(Type(Inner::Int2Vector)) - } - Inner::Int4Array => { - &Kind::Array(Type(Inner::Int4)) - } - Inner::RegprocArray => { - &Kind::Array(Type(Inner::Regproc)) - } - Inner::TextArray => { - &Kind::Array(Type(Inner::Text)) - } - Inner::TidArray => { - &Kind::Array(Type(Inner::Tid)) - } - Inner::XidArray => { - &Kind::Array(Type(Inner::Xid)) - } - Inner::CidArray => { - &Kind::Array(Type(Inner::Cid)) - } - Inner::OidVectorArray => { - &Kind::Array(Type(Inner::OidVector)) - } - Inner::BpcharArray => { - &Kind::Array(Type(Inner::Bpchar)) - } - Inner::VarcharArray => { - &Kind::Array(Type(Inner::Varchar)) - } - Inner::Int8Array => { - &Kind::Array(Type(Inner::Int8)) - } - Inner::PointArray => { - &Kind::Array(Type(Inner::Point)) - } - Inner::LsegArray => { - &Kind::Array(Type(Inner::Lseg)) - } - Inner::PathArray => { - &Kind::Array(Type(Inner::Path)) - } - Inner::BoxArray => { - &Kind::Array(Type(Inner::Box)) - } - Inner::Float4Array => { - &Kind::Array(Type(Inner::Float4)) - } - Inner::Float8Array => { - &Kind::Array(Type(Inner::Float8)) - } - Inner::PolygonArray => { - &Kind::Array(Type(Inner::Polygon)) - } - Inner::OidArray => { - &Kind::Array(Type(Inner::Oid)) - } - Inner::Aclitem => { - &Kind::Simple - } - Inner::AclitemArray => { - &Kind::Array(Type(Inner::Aclitem)) - } - Inner::MacaddrArray => { - &Kind::Array(Type(Inner::Macaddr)) - } - Inner::InetArray => { - &Kind::Array(Type(Inner::Inet)) - } - Inner::Bpchar => { - &Kind::Simple - } - Inner::Varchar => { - &Kind::Simple - } - Inner::Date => { - &Kind::Simple - } - Inner::Time => { - &Kind::Simple - } - Inner::Timestamp => { - &Kind::Simple - } - Inner::TimestampArray => { - &Kind::Array(Type(Inner::Timestamp)) - } - Inner::DateArray => { - &Kind::Array(Type(Inner::Date)) - } - Inner::TimeArray => { - &Kind::Array(Type(Inner::Time)) - } - Inner::Timestamptz => { - &Kind::Simple - } - Inner::TimestamptzArray => { - &Kind::Array(Type(Inner::Timestamptz)) - } - Inner::Interval => { - &Kind::Simple - } - Inner::IntervalArray => { - &Kind::Array(Type(Inner::Interval)) - } - Inner::NumericArray => { - &Kind::Array(Type(Inner::Numeric)) - } - Inner::CstringArray => { - &Kind::Array(Type(Inner::Cstring)) - } - Inner::Timetz => { - &Kind::Simple - } - Inner::TimetzArray => { - &Kind::Array(Type(Inner::Timetz)) - } - Inner::Bit => { - &Kind::Simple - } - Inner::BitArray => { - &Kind::Array(Type(Inner::Bit)) - } - Inner::Varbit => { - &Kind::Simple - } - Inner::VarbitArray => { - &Kind::Array(Type(Inner::Varbit)) - } - Inner::Numeric => { - &Kind::Simple - } - Inner::Refcursor => { - &Kind::Simple - } - Inner::RefcursorArray => { - &Kind::Array(Type(Inner::Refcursor)) - } - Inner::Regprocedure => { - &Kind::Simple - } - Inner::Regoper => { - &Kind::Simple - } - Inner::Regoperator => { - &Kind::Simple - } - Inner::Regclass => { - &Kind::Simple - } - Inner::Regtype => { - &Kind::Simple - } - Inner::RegprocedureArray => { - &Kind::Array(Type(Inner::Regprocedure)) - } - Inner::RegoperArray => { - &Kind::Array(Type(Inner::Regoper)) - } - Inner::RegoperatorArray => { - &Kind::Array(Type(Inner::Regoperator)) - } - Inner::RegclassArray => { - &Kind::Array(Type(Inner::Regclass)) - } - Inner::RegtypeArray => { - &Kind::Array(Type(Inner::Regtype)) - } - Inner::Record => { - &Kind::Pseudo - } - Inner::Cstring => { - &Kind::Pseudo - } - Inner::Any => { - &Kind::Pseudo - } - Inner::Anyarray => { - &Kind::Pseudo - } - Inner::Void => { - &Kind::Pseudo - } - Inner::Trigger => { - &Kind::Pseudo - } - Inner::LanguageHandler => { - &Kind::Pseudo - } - Inner::Internal => { - &Kind::Pseudo - } - Inner::Anyelement => { - &Kind::Pseudo - } - Inner::RecordArray => { - &Kind::Pseudo - } - Inner::Anynonarray => { - &Kind::Pseudo - } - Inner::TxidSnapshotArray => { - &Kind::Array(Type(Inner::TxidSnapshot)) - } - Inner::Uuid => { - &Kind::Simple - } - Inner::UuidArray => { - &Kind::Array(Type(Inner::Uuid)) - } - Inner::TxidSnapshot => { - &Kind::Simple - } - Inner::FdwHandler => { - &Kind::Pseudo - } - Inner::PgLsn => { - &Kind::Simple - } - Inner::PgLsnArray => { - &Kind::Array(Type(Inner::PgLsn)) - } - Inner::TsmHandler => { - &Kind::Pseudo - } - Inner::PgNdistinct => { - &Kind::Simple - } - Inner::PgDependencies => { - &Kind::Simple - } - Inner::Anyenum => { - &Kind::Pseudo - } - Inner::TsVector => { - &Kind::Simple - } - Inner::Tsquery => { - &Kind::Simple - } - Inner::GtsVector => { - &Kind::Simple - } - Inner::TsVectorArray => { - &Kind::Array(Type(Inner::TsVector)) - } - Inner::GtsVectorArray => { - &Kind::Array(Type(Inner::GtsVector)) - } - Inner::TsqueryArray => { - &Kind::Array(Type(Inner::Tsquery)) - } - Inner::Regconfig => { - &Kind::Simple - } - Inner::RegconfigArray => { - &Kind::Array(Type(Inner::Regconfig)) - } - Inner::Regdictionary => { - &Kind::Simple - } - Inner::RegdictionaryArray => { - &Kind::Array(Type(Inner::Regdictionary)) - } - Inner::Jsonb => { - &Kind::Simple - } - Inner::JsonbArray => { - &Kind::Array(Type(Inner::Jsonb)) - } - Inner::AnyRange => { - &Kind::Pseudo - } - Inner::EventTrigger => { - &Kind::Pseudo - } - Inner::Int4Range => { - &Kind::Range(Type(Inner::Int4)) - } - Inner::Int4RangeArray => { - &Kind::Array(Type(Inner::Int4Range)) - } - Inner::NumRange => { - &Kind::Range(Type(Inner::Numeric)) - } - Inner::NumRangeArray => { - &Kind::Array(Type(Inner::NumRange)) - } - Inner::TsRange => { - &Kind::Range(Type(Inner::Timestamp)) - } - Inner::TsRangeArray => { - &Kind::Array(Type(Inner::TsRange)) - } - Inner::TstzRange => { - &Kind::Range(Type(Inner::Timestamptz)) - } - Inner::TstzRangeArray => { - &Kind::Array(Type(Inner::TstzRange)) - } - Inner::DateRange => { - &Kind::Range(Type(Inner::Date)) - } - Inner::DateRangeArray => { - &Kind::Array(Type(Inner::DateRange)) - } - Inner::Int8Range => { - &Kind::Range(Type(Inner::Int8)) - } - Inner::Int8RangeArray => { - &Kind::Array(Type(Inner::Int8Range)) - } - Inner::Jsonpath => { - &Kind::Simple - } - Inner::JsonpathArray => { - &Kind::Array(Type(Inner::Jsonpath)) - } - Inner::Regnamespace => { - &Kind::Simple - } - Inner::RegnamespaceArray => { - &Kind::Array(Type(Inner::Regnamespace)) - } - Inner::Regrole => { - &Kind::Simple - } - Inner::RegroleArray => { - &Kind::Array(Type(Inner::Regrole)) - } - Inner::Regcollation => { - &Kind::Simple - } - Inner::RegcollationArray => { - &Kind::Array(Type(Inner::Regcollation)) - } - Inner::Int4multiRange => { - &Kind::Range(Type(Inner::Int4)) - } - Inner::NummultiRange => { - &Kind::Range(Type(Inner::Numeric)) - } - Inner::TsmultiRange => { - &Kind::Range(Type(Inner::Timestamp)) - } - Inner::TstzmultiRange => { - &Kind::Range(Type(Inner::Timestamptz)) - } - Inner::DatemultiRange => { - &Kind::Range(Type(Inner::Date)) - } - Inner::Int8multiRange => { - &Kind::Range(Type(Inner::Int8)) - } - Inner::AnymultiRange => { - &Kind::Pseudo - } - Inner::AnycompatiblemultiRange => { - &Kind::Pseudo - } - Inner::PgBrinBloomSummary => { - &Kind::Simple - } - Inner::PgBrinMinmaxMultiSummary => { - &Kind::Simple - } - Inner::PgMcvList => { - &Kind::Simple - } - Inner::PgSnapshot => { - &Kind::Simple - } - Inner::PgSnapshotArray => { - &Kind::Array(Type(Inner::PgSnapshot)) - } - Inner::Xid8 => { - &Kind::Simple - } - Inner::Anycompatible => { - &Kind::Pseudo - } - Inner::Anycompatiblearray => { - &Kind::Pseudo - } - Inner::Anycompatiblenonarray => { - &Kind::Pseudo - } - Inner::AnycompatibleRange => { - &Kind::Pseudo - } - Inner::Int4multiRangeArray => { - &Kind::Array(Type(Inner::Int4multiRange)) - } - Inner::NummultiRangeArray => { - &Kind::Array(Type(Inner::NummultiRange)) - } - Inner::TsmultiRangeArray => { - &Kind::Array(Type(Inner::TsmultiRange)) - } - Inner::TstzmultiRangeArray => { - &Kind::Array(Type(Inner::TstzmultiRange)) - } - Inner::DatemultiRangeArray => { - &Kind::Array(Type(Inner::DatemultiRange)) - } - Inner::Int8multiRangeArray => { - &Kind::Array(Type(Inner::Int8multiRange)) - } + Inner::Bool => &Kind::Simple, + Inner::Bytea => &Kind::Simple, + Inner::Char => &Kind::Simple, + Inner::Name => &Kind::Simple, + Inner::Int8 => &Kind::Simple, + Inner::Int2 => &Kind::Simple, + Inner::Int2Vector => &Kind::Array(Type(Inner::Int2)), + Inner::Int4 => &Kind::Simple, + Inner::Regproc => &Kind::Simple, + Inner::Text => &Kind::Simple, + Inner::Oid => &Kind::Simple, + Inner::Tid => &Kind::Simple, + Inner::Xid => &Kind::Simple, + Inner::Cid => &Kind::Simple, + Inner::OidVector => &Kind::Array(Type(Inner::Oid)), + Inner::PgDdlCommand => &Kind::Pseudo, + Inner::Json => &Kind::Simple, + Inner::Xml => &Kind::Simple, + Inner::XmlArray => &Kind::Array(Type(Inner::Xml)), + Inner::PgNodeTree => &Kind::Simple, + Inner::JsonArray => &Kind::Array(Type(Inner::Json)), + Inner::TableAmHandler => &Kind::Pseudo, + Inner::Xid8Array => &Kind::Array(Type(Inner::Xid8)), + Inner::IndexAmHandler => &Kind::Pseudo, + Inner::Point => &Kind::Simple, + Inner::Lseg => &Kind::Simple, + Inner::Path => &Kind::Simple, + Inner::Box => &Kind::Simple, + Inner::Polygon => &Kind::Simple, + Inner::Line => &Kind::Simple, + Inner::LineArray => &Kind::Array(Type(Inner::Line)), + Inner::Cidr => &Kind::Simple, + Inner::CidrArray => &Kind::Array(Type(Inner::Cidr)), + Inner::Float4 => &Kind::Simple, + Inner::Float8 => &Kind::Simple, + Inner::Unknown => &Kind::Simple, + Inner::Circle => &Kind::Simple, + Inner::CircleArray => &Kind::Array(Type(Inner::Circle)), + Inner::Macaddr8 => &Kind::Simple, + Inner::Macaddr8Array => &Kind::Array(Type(Inner::Macaddr8)), + Inner::Money => &Kind::Simple, + Inner::MoneyArray => &Kind::Array(Type(Inner::Money)), + Inner::Macaddr => &Kind::Simple, + Inner::Inet => &Kind::Simple, + Inner::BoolArray => &Kind::Array(Type(Inner::Bool)), + Inner::ByteaArray => &Kind::Array(Type(Inner::Bytea)), + Inner::CharArray => &Kind::Array(Type(Inner::Char)), + Inner::NameArray => &Kind::Array(Type(Inner::Name)), + Inner::Int2Array => &Kind::Array(Type(Inner::Int2)), + Inner::Int2VectorArray => &Kind::Array(Type(Inner::Int2Vector)), + Inner::Int4Array => &Kind::Array(Type(Inner::Int4)), + Inner::RegprocArray => &Kind::Array(Type(Inner::Regproc)), + Inner::TextArray => &Kind::Array(Type(Inner::Text)), + Inner::TidArray => &Kind::Array(Type(Inner::Tid)), + Inner::XidArray => &Kind::Array(Type(Inner::Xid)), + Inner::CidArray => &Kind::Array(Type(Inner::Cid)), + Inner::OidVectorArray => &Kind::Array(Type(Inner::OidVector)), + Inner::BpcharArray => &Kind::Array(Type(Inner::Bpchar)), + Inner::VarcharArray => &Kind::Array(Type(Inner::Varchar)), + Inner::Int8Array => &Kind::Array(Type(Inner::Int8)), + Inner::PointArray => &Kind::Array(Type(Inner::Point)), + Inner::LsegArray => &Kind::Array(Type(Inner::Lseg)), + Inner::PathArray => &Kind::Array(Type(Inner::Path)), + Inner::BoxArray => &Kind::Array(Type(Inner::Box)), + Inner::Float4Array => &Kind::Array(Type(Inner::Float4)), + Inner::Float8Array => &Kind::Array(Type(Inner::Float8)), + Inner::PolygonArray => &Kind::Array(Type(Inner::Polygon)), + Inner::OidArray => &Kind::Array(Type(Inner::Oid)), + Inner::Aclitem => &Kind::Simple, + Inner::AclitemArray => &Kind::Array(Type(Inner::Aclitem)), + Inner::MacaddrArray => &Kind::Array(Type(Inner::Macaddr)), + Inner::InetArray => &Kind::Array(Type(Inner::Inet)), + Inner::Bpchar => &Kind::Simple, + Inner::Varchar => &Kind::Simple, + Inner::Date => &Kind::Simple, + Inner::Time => &Kind::Simple, + Inner::Timestamp => &Kind::Simple, + Inner::TimestampArray => &Kind::Array(Type(Inner::Timestamp)), + Inner::DateArray => &Kind::Array(Type(Inner::Date)), + Inner::TimeArray => &Kind::Array(Type(Inner::Time)), + Inner::Timestamptz => &Kind::Simple, + Inner::TimestamptzArray => &Kind::Array(Type(Inner::Timestamptz)), + Inner::Interval => &Kind::Simple, + Inner::IntervalArray => &Kind::Array(Type(Inner::Interval)), + Inner::NumericArray => &Kind::Array(Type(Inner::Numeric)), + Inner::CstringArray => &Kind::Array(Type(Inner::Cstring)), + Inner::Timetz => &Kind::Simple, + Inner::TimetzArray => &Kind::Array(Type(Inner::Timetz)), + Inner::Bit => &Kind::Simple, + Inner::BitArray => &Kind::Array(Type(Inner::Bit)), + Inner::Varbit => &Kind::Simple, + Inner::VarbitArray => &Kind::Array(Type(Inner::Varbit)), + Inner::Numeric => &Kind::Simple, + Inner::Refcursor => &Kind::Simple, + Inner::RefcursorArray => &Kind::Array(Type(Inner::Refcursor)), + Inner::Regprocedure => &Kind::Simple, + Inner::Regoper => &Kind::Simple, + Inner::Regoperator => &Kind::Simple, + Inner::Regclass => &Kind::Simple, + Inner::Regtype => &Kind::Simple, + Inner::RegprocedureArray => &Kind::Array(Type(Inner::Regprocedure)), + Inner::RegoperArray => &Kind::Array(Type(Inner::Regoper)), + Inner::RegoperatorArray => &Kind::Array(Type(Inner::Regoperator)), + Inner::RegclassArray => &Kind::Array(Type(Inner::Regclass)), + Inner::RegtypeArray => &Kind::Array(Type(Inner::Regtype)), + Inner::Record => &Kind::Pseudo, + Inner::Cstring => &Kind::Pseudo, + Inner::Any => &Kind::Pseudo, + Inner::Anyarray => &Kind::Pseudo, + Inner::Void => &Kind::Pseudo, + Inner::Trigger => &Kind::Pseudo, + Inner::LanguageHandler => &Kind::Pseudo, + Inner::Internal => &Kind::Pseudo, + Inner::Anyelement => &Kind::Pseudo, + Inner::RecordArray => &Kind::Pseudo, + Inner::Anynonarray => &Kind::Pseudo, + Inner::TxidSnapshotArray => &Kind::Array(Type(Inner::TxidSnapshot)), + Inner::Uuid => &Kind::Simple, + Inner::UuidArray => &Kind::Array(Type(Inner::Uuid)), + Inner::TxidSnapshot => &Kind::Simple, + Inner::FdwHandler => &Kind::Pseudo, + Inner::PgLsn => &Kind::Simple, + Inner::PgLsnArray => &Kind::Array(Type(Inner::PgLsn)), + Inner::TsmHandler => &Kind::Pseudo, + Inner::PgNdistinct => &Kind::Simple, + Inner::PgDependencies => &Kind::Simple, + Inner::Anyenum => &Kind::Pseudo, + Inner::TsVector => &Kind::Simple, + Inner::Tsquery => &Kind::Simple, + Inner::GtsVector => &Kind::Simple, + Inner::TsVectorArray => &Kind::Array(Type(Inner::TsVector)), + Inner::GtsVectorArray => &Kind::Array(Type(Inner::GtsVector)), + Inner::TsqueryArray => &Kind::Array(Type(Inner::Tsquery)), + Inner::Regconfig => &Kind::Simple, + Inner::RegconfigArray => &Kind::Array(Type(Inner::Regconfig)), + Inner::Regdictionary => &Kind::Simple, + Inner::RegdictionaryArray => &Kind::Array(Type(Inner::Regdictionary)), + Inner::Jsonb => &Kind::Simple, + Inner::JsonbArray => &Kind::Array(Type(Inner::Jsonb)), + Inner::AnyRange => &Kind::Pseudo, + Inner::EventTrigger => &Kind::Pseudo, + Inner::Int4Range => &Kind::Range(Type(Inner::Int4)), + Inner::Int4RangeArray => &Kind::Array(Type(Inner::Int4Range)), + Inner::NumRange => &Kind::Range(Type(Inner::Numeric)), + Inner::NumRangeArray => &Kind::Array(Type(Inner::NumRange)), + Inner::TsRange => &Kind::Range(Type(Inner::Timestamp)), + Inner::TsRangeArray => &Kind::Array(Type(Inner::TsRange)), + Inner::TstzRange => &Kind::Range(Type(Inner::Timestamptz)), + Inner::TstzRangeArray => &Kind::Array(Type(Inner::TstzRange)), + Inner::DateRange => &Kind::Range(Type(Inner::Date)), + Inner::DateRangeArray => &Kind::Array(Type(Inner::DateRange)), + Inner::Int8Range => &Kind::Range(Type(Inner::Int8)), + Inner::Int8RangeArray => &Kind::Array(Type(Inner::Int8Range)), + Inner::Jsonpath => &Kind::Simple, + Inner::JsonpathArray => &Kind::Array(Type(Inner::Jsonpath)), + Inner::Regnamespace => &Kind::Simple, + Inner::RegnamespaceArray => &Kind::Array(Type(Inner::Regnamespace)), + Inner::Regrole => &Kind::Simple, + Inner::RegroleArray => &Kind::Array(Type(Inner::Regrole)), + Inner::Regcollation => &Kind::Simple, + Inner::RegcollationArray => &Kind::Array(Type(Inner::Regcollation)), + Inner::Int4multiRange => &Kind::Multirange(Type(Inner::Int4)), + Inner::NummultiRange => &Kind::Multirange(Type(Inner::Numeric)), + Inner::TsmultiRange => &Kind::Multirange(Type(Inner::Timestamp)), + Inner::TstzmultiRange => &Kind::Multirange(Type(Inner::Timestamptz)), + Inner::DatemultiRange => &Kind::Multirange(Type(Inner::Date)), + Inner::Int8multiRange => &Kind::Multirange(Type(Inner::Int8)), + Inner::AnymultiRange => &Kind::Pseudo, + Inner::AnycompatiblemultiRange => &Kind::Pseudo, + Inner::PgBrinBloomSummary => &Kind::Simple, + Inner::PgBrinMinmaxMultiSummary => &Kind::Simple, + Inner::PgMcvList => &Kind::Simple, + Inner::PgSnapshot => &Kind::Simple, + Inner::PgSnapshotArray => &Kind::Array(Type(Inner::PgSnapshot)), + Inner::Xid8 => &Kind::Simple, + Inner::Anycompatible => &Kind::Pseudo, + Inner::Anycompatiblearray => &Kind::Pseudo, + Inner::Anycompatiblenonarray => &Kind::Pseudo, + Inner::AnycompatibleRange => &Kind::Pseudo, + Inner::Int4multiRangeArray => &Kind::Array(Type(Inner::Int4multiRange)), + Inner::NummultiRangeArray => &Kind::Array(Type(Inner::NummultiRange)), + Inner::TsmultiRangeArray => &Kind::Array(Type(Inner::TsmultiRange)), + Inner::TstzmultiRangeArray => &Kind::Array(Type(Inner::TstzmultiRange)), + Inner::DatemultiRangeArray => &Kind::Array(Type(Inner::DatemultiRange)), + Inner::Int8multiRangeArray => &Kind::Array(Type(Inner::Int8multiRange)), Inner::Other(ref u) => &u.kind, } } @@ -1891,4 +1521,4 @@ impl Type { /// INT8MULTIRANGE[] pub const INT8MULTI_RANGE_ARRAY: Type = Type(Inner::Int8multiRangeArray); -} \ No newline at end of file +} diff --git a/tokio-postgres/src/error/sqlstate.rs b/tokio-postgres/src/error/sqlstate.rs index 6f191fc16..13a1d75f9 100644 --- a/tokio-postgres/src/error/sqlstate.rs +++ b/tokio-postgres/src/error/sqlstate.rs @@ -279,7 +279,7 @@ impl SqlState { Inner::Other(code) => code, } } - + /// 00000 pub const SUCCESSFUL_COMPLETION: SqlState = SqlState(Inner::E00000); @@ -365,7 +365,8 @@ impl SqlState { pub const DIAGNOSTICS_EXCEPTION: SqlState = SqlState(Inner::E0Z000); /// 0Z002 - pub const STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER: SqlState = SqlState(Inner::E0Z002); + pub const STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER: SqlState = + SqlState(Inner::E0Z002); /// 20000 pub const CASE_NOT_FOUND: SqlState = SqlState(Inner::E20000); @@ -623,7 +624,8 @@ impl SqlState { pub const INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION: SqlState = SqlState(Inner::E25003); /// 25004 - pub const INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION: SqlState = SqlState(Inner::E25004); + pub const INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION: SqlState = + SqlState(Inner::E25004); /// 25005 pub const NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION: SqlState = SqlState(Inner::E25005); @@ -1344,7 +1346,7 @@ enum Inner { EXX002, Other(Box), } - + #[rustfmt::skip] static SQLSTATE_MAP: phf::Map<&'static str, SqlState> = ::phf::Map { From eaa1c6279393ce8f0967cce7587638bb37135765 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Nov 2022 13:05:23 +0000 Subject: [PATCH 016/207] Update env_logger requirement from 0.9 to 0.10 Updates the requirements on [env_logger](https://github.com/rust-cli/env_logger) to permit the latest version. - [Release notes](https://github.com/rust-cli/env_logger/releases) - [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-cli/env_logger/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: env_logger dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- tokio-postgres/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 760105104..68737f738 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -62,7 +62,7 @@ tokio-util = { version = "0.7", features = ["codec"] } [dev-dependencies] futures-executor = "0.3" criterion = "0.4" -env_logger = "0.9" +env_logger = "0.10" tokio = { version = "1.0", features = [ "macros", "net", From be90d0e848c0be8a6461127b81c7ad966c828ad0 Mon Sep 17 00:00:00 2001 From: xxchan Date: Wed, 7 Dec 2022 12:29:28 +0100 Subject: [PATCH 017/207] derive Debug for SimpleQueryMessage --- postgres-protocol/src/message/backend.rs | 1 + tokio-postgres/src/lib.rs | 1 + tokio-postgres/src/row.rs | 1 + tokio-postgres/src/simple_query.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/postgres-protocol/src/message/backend.rs b/postgres-protocol/src/message/backend.rs index 45e5c4074..3f5374d64 100644 --- a/postgres-protocol/src/message/backend.rs +++ b/postgres-protocol/src/message/backend.rs @@ -524,6 +524,7 @@ impl CopyOutResponseBody { } } +#[derive(Debug)] pub struct DataRowBody { storage: Bytes, len: u16, diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs index bd4d7b8ce..a9ecba4f1 100644 --- a/tokio-postgres/src/lib.rs +++ b/tokio-postgres/src/lib.rs @@ -239,6 +239,7 @@ pub enum AsyncMessage { } /// Message returned by the `SimpleQuery` stream. +#[derive(Debug)] #[non_exhaustive] pub enum SimpleQueryMessage { /// A row of data. diff --git a/tokio-postgres/src/row.rs b/tokio-postgres/src/row.rs index e3ed696c1..db179b432 100644 --- a/tokio-postgres/src/row.rs +++ b/tokio-postgres/src/row.rs @@ -196,6 +196,7 @@ impl AsName for SimpleColumn { } /// A row of data returned from the database by a simple query. +#[derive(Debug)] pub struct SimpleQueryRow { columns: Arc<[SimpleColumn]>, body: DataRowBody, diff --git a/tokio-postgres/src/simple_query.rs b/tokio-postgres/src/simple_query.rs index 19cb10236..7c266e409 100644 --- a/tokio-postgres/src/simple_query.rs +++ b/tokio-postgres/src/simple_query.rs @@ -15,6 +15,7 @@ use std::sync::Arc; use std::task::{Context, Poll}; /// Information about a column of a single query row. +#[derive(Debug)] pub struct SimpleColumn { name: String, } From 748167d5f8a489888209f58492153f52bd9d9e27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 13:07:35 +0000 Subject: [PATCH 018/207] Update base64 requirement from 0.13 to 0.20 Updates the requirements on [base64](https://github.com/marshallpierce/rust-base64) to permit the latest version. - [Release notes](https://github.com/marshallpierce/rust-base64/releases) - [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md) - [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.13.0...v0.20.0) --- updated-dependencies: - dependency-name: base64 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- postgres-protocol/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index a4716907b..2a72cc60c 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" [dependencies] -base64 = "0.13" +base64 = "0.20" byteorder = "1.0" bytes = "1.0" fallible-iterator = "0.2" From 3697f6b63c67073925e1db4d5bb74f1a4dc8c3f3 Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Fri, 26 Aug 2022 13:46:04 -0700 Subject: [PATCH 019/207] Add hostaddr support --- tokio-postgres/src/config.rs | 70 +++++++++++++++++++++++++++++++++++ tokio-postgres/src/connect.rs | 23 +++++++++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 5b364ec06..0c62b5030 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -13,6 +13,7 @@ use crate::{Client, Connection, Error}; use std::borrow::Cow; #[cfg(unix)] use std::ffi::OsStr; +use std::ops::Deref; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; #[cfg(unix)] @@ -91,6 +92,17 @@ 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. +/// * `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, +/// - or if host specifies an IP address, that value will be used directly. +/// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications +/// with time constraints. However, a host name is required for verify-full SSL certificate verification. +/// Note that `host` is always required regardless of whether `hostaddr` is present. +/// * If `host` is specified without `hostaddr`, a host name lookup occurs; +/// * If both `host` and `hostaddr` are specified, the value for `hostaddr` gives the server network address. +/// The value for `host` is ignored unless the authentication method requires it, +/// in which case it will be used as the host name. /// * `port` - The port to connect to. Multiple ports can be specified, separated by commas. The number of ports must be /// either 1, in which case it will be used for all hosts, or the same as the number of hosts. Defaults to 5432 if /// omitted or the empty string. @@ -122,6 +134,10 @@ pub enum Host { /// ``` /// /// ```not_rust +/// host=host1,host2,host3 port=1234,,5678 hostaddr=127.0.0.1,127.0.0.2,127.0.0.3 user=postgres target_session_attrs=read-write +/// ``` +/// +/// ```not_rust /// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write /// ``` /// @@ -158,6 +174,7 @@ pub struct Config { pub(crate) application_name: Option, pub(crate) ssl_mode: SslMode, pub(crate) host: Vec, + pub(crate) hostaddr: Vec, pub(crate) port: Vec, pub(crate) connect_timeout: Option, pub(crate) keepalives: bool, @@ -188,6 +205,7 @@ impl Config { application_name: None, ssl_mode: SslMode::Prefer, host: vec![], + hostaddr: vec![], port: vec![], connect_timeout: None, keepalives: true, @@ -298,6 +316,11 @@ impl Config { &self.host } + /// Gets the hostaddrs that have been added to the configuration with `hostaddr`. + pub fn get_hostaddrs(&self) -> &[String] { + self.hostaddr.deref() + } + /// Adds a Unix socket host to the configuration. /// /// Unlike `host`, this method allows non-UTF8 paths. @@ -310,6 +333,15 @@ impl Config { self } + /// Adds a hostaddr to the configuration. + /// + /// Multiple hostaddrs can be specified by calling this method multiple times, and each will be tried in order. + /// There must be either no hostaddrs, or the same number of hostaddrs as hosts. + pub fn hostaddr(&mut self, hostaddr: &str) -> &mut Config { + self.hostaddr.push(hostaddr.to_string()); + self + } + /// Adds a port to the configuration. /// /// Multiple ports can be specified by calling this method multiple times. There must either be no ports, in which @@ -455,6 +487,11 @@ impl Config { self.host(host); } } + "hostaddr" => { + for hostaddr in value.split(',') { + self.hostaddr(hostaddr); + } + } "port" => { for port in value.split(',') { let port = if port.is_empty() { @@ -593,6 +630,7 @@ impl fmt::Debug for Config { .field("application_name", &self.application_name) .field("ssl_mode", &self.ssl_mode) .field("host", &self.host) + .field("hostaddr", &self.hostaddr) .field("port", &self.port) .field("connect_timeout", &self.connect_timeout) .field("keepalives", &self.keepalives) @@ -975,3 +1013,35 @@ impl<'a> UrlParser<'a> { .map_err(|e| Error::config_parse(e.into())) } } + +#[cfg(test)] +mod tests { + use crate::{config::Host, Config}; + + #[test] + fn test_simple_parsing() { + let s = "user=pass_user dbname=postgres host=host1,host2 hostaddr=127.0.0.1,127.0.0.2 port=26257"; + let config = s.parse::().unwrap(); + assert_eq!(Some("pass_user"), config.get_user()); + assert_eq!(Some("postgres"), config.get_dbname()); + assert_eq!( + [ + Host::Tcp("host1".to_string()), + Host::Tcp("host2".to_string()) + ], + config.get_hosts(), + ); + + assert_eq!(["127.0.0.1", "127.0.0.2"], config.get_hostaddrs(),); + + assert_eq!(1, 1); + } + + #[test] + fn test_empty_hostaddrs() { + let s = + "user=pass_user dbname=postgres host=host1,host2,host3 hostaddr=127.0.0.1,,127.0.0.2"; + let config = s.parse::().unwrap(); + assert_eq!(["127.0.0.1", "", "127.0.0.2"], config.get_hostaddrs(),); + } +} diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 97a00c812..c36677234 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -23,6 +23,15 @@ where return Err(Error::config("invalid number of ports".into())); } + if !config.hostaddr.is_empty() && config.hostaddr.len() != config.host.len() { + let msg = format!( + "invalid number of hostaddrs ({}). Possible values: 0 or number of hosts ({})", + config.hostaddr.len(), + config.host.len(), + ); + return Err(Error::config(msg.into())); + } + let mut error = None; for (i, host) in config.host.iter().enumerate() { let port = config @@ -32,18 +41,28 @@ where .copied() .unwrap_or(5432); + // The value of host is always used as the hostname for TLS validation. let hostname = match host { Host::Tcp(host) => host.as_str(), // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter #[cfg(unix)] Host::Unix(_) => "", }; - let tls = tls .make_tls_connect(hostname) .map_err(|e| Error::tls(e.into()))?; - match connect_once(host, port, tls, config).await { + // If both host and hostaddr are specified, the value of hostaddr is used to to establish the TCP connection. + let hostaddr = match host { + Host::Tcp(_hostname) => match config.hostaddr.get(i) { + Some(hostaddr) if hostaddr.is_empty() => Host::Tcp(hostaddr.clone()), + _ => host.clone(), + }, + #[cfg(unix)] + Host::Unix(_v) => host.clone(), + }; + + match connect_once(&hostaddr, port, tls, config).await { Ok((client, connection)) => return Ok((client, connection)), Err(e) => error = Some(e), } From 48874dc5753e33f49508ba986d7f1d7bc74b4a74 Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sat, 27 Aug 2022 11:40:57 -0700 Subject: [PATCH 020/207] IpAddr + try hostaddr first --- tokio-postgres/src/config.rs | 36 ++++++++++-------- tokio-postgres/src/connect.rs | 61 +++++++++++++++++++------------ tokio-postgres/tests/test/main.rs | 52 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 39 deletions(-) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 0c62b5030..34accdbe8 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -13,6 +13,7 @@ use crate::{Client, Connection, Error}; use std::borrow::Cow; #[cfg(unix)] use std::ffi::OsStr; +use std::net::IpAddr; use std::ops::Deref; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; @@ -98,7 +99,9 @@ pub enum Host { /// - or if host specifies an IP address, that value will be used directly. /// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications /// with time constraints. However, a host name is required for verify-full SSL certificate verification. -/// Note that `host` is always required regardless of whether `hostaddr` is present. +/// Specifically: +/// * If `hostaddr` is specified without `host`, the value for `hostaddr` gives the server network address. +/// The connection attempt will fail if the authentication method requires a host name; /// * If `host` is specified without `hostaddr`, a host name lookup occurs; /// * If both `host` and `hostaddr` are specified, the value for `hostaddr` gives the server network address. /// The value for `host` is ignored unless the authentication method requires it, @@ -174,7 +177,7 @@ pub struct Config { pub(crate) application_name: Option, pub(crate) ssl_mode: SslMode, pub(crate) host: Vec, - pub(crate) hostaddr: Vec, + pub(crate) hostaddr: Vec, pub(crate) port: Vec, pub(crate) connect_timeout: Option, pub(crate) keepalives: bool, @@ -317,7 +320,7 @@ impl Config { } /// Gets the hostaddrs that have been added to the configuration with `hostaddr`. - pub fn get_hostaddrs(&self) -> &[String] { + pub fn get_hostaddrs(&self) -> &[IpAddr] { self.hostaddr.deref() } @@ -337,8 +340,8 @@ impl Config { /// /// Multiple hostaddrs can be specified by calling this method multiple times, and each will be tried in order. /// There must be either no hostaddrs, or the same number of hostaddrs as hosts. - pub fn hostaddr(&mut self, hostaddr: &str) -> &mut Config { - self.hostaddr.push(hostaddr.to_string()); + pub fn hostaddr(&mut self, hostaddr: IpAddr) -> &mut Config { + self.hostaddr.push(hostaddr); self } @@ -489,7 +492,10 @@ impl Config { } "hostaddr" => { for hostaddr in value.split(',') { - self.hostaddr(hostaddr); + let addr = hostaddr + .parse() + .map_err(|_| Error::config_parse(Box::new(InvalidValue("hostaddr"))))?; + self.hostaddr(addr); } } "port" => { @@ -1016,6 +1022,8 @@ impl<'a> UrlParser<'a> { #[cfg(test)] mod tests { + use std::net::IpAddr; + use crate::{config::Host, Config}; #[test] @@ -1032,16 +1040,14 @@ mod tests { config.get_hosts(), ); - assert_eq!(["127.0.0.1", "127.0.0.2"], config.get_hostaddrs(),); + assert_eq!( + [ + "127.0.0.1".parse::().unwrap(), + "127.0.0.2".parse::().unwrap() + ], + config.get_hostaddrs(), + ); assert_eq!(1, 1); } - - #[test] - fn test_empty_hostaddrs() { - let s = - "user=pass_user dbname=postgres host=host1,host2,host3 hostaddr=127.0.0.1,,127.0.0.2"; - let config = s.parse::().unwrap(); - assert_eq!(["127.0.0.1", "", "127.0.0.2"], config.get_hostaddrs(),); - } } diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index c36677234..ee1dc1c76 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -5,8 +5,8 @@ use crate::connect_socket::connect_socket; use crate::tls::{MakeTlsConnect, TlsConnect}; use crate::{Client, Config, Connection, Error, SimpleQueryMessage, Socket}; use futures_util::{future, pin_mut, Future, FutureExt, Stream}; -use std::io; use std::task::Poll; +use std::{cmp, io}; pub async fn connect( mut tls: T, @@ -15,25 +15,35 @@ pub async fn connect( where T: MakeTlsConnect, { - if config.host.is_empty() { - return Err(Error::config("host missing".into())); + if config.host.is_empty() && config.hostaddr.is_empty() { + return Err(Error::config("both host and hostaddr are missing".into())); } - if config.port.len() > 1 && config.port.len() != config.host.len() { - return Err(Error::config("invalid number of ports".into())); - } - - if !config.hostaddr.is_empty() && config.hostaddr.len() != config.host.len() { + if !config.host.is_empty() + && !config.hostaddr.is_empty() + && config.host.len() != config.hostaddr.len() + { let msg = format!( - "invalid number of hostaddrs ({}). Possible values: 0 or number of hosts ({})", - config.hostaddr.len(), + "number of hosts ({}) is different from number of hostaddrs ({})", config.host.len(), + config.hostaddr.len(), ); return Err(Error::config(msg.into())); } + // At this point, either one of the following two scenarios could happen: + // (1) either config.host or config.hostaddr must be empty; + // (2) if both config.host and config.hostaddr are NOT empty; their lengths must be equal. + let num_hosts = cmp::max(config.host.len(), config.hostaddr.len()); + + if config.port.len() > 1 && config.port.len() != num_hosts { + return Err(Error::config("invalid number of ports".into())); + } + let mut error = None; - for (i, host) in config.host.iter().enumerate() { + for i in 0..num_hosts { + let host = config.host.get(i); + let hostaddr = config.hostaddr.get(i); let port = config .port .get(i) @@ -42,27 +52,30 @@ where .unwrap_or(5432); // The value of host is always used as the hostname for TLS validation. + // postgres doesn't support TLS over unix sockets, so the choice for Host::Unix variant here doesn't matter let hostname = match host { - Host::Tcp(host) => host.as_str(), - // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter - #[cfg(unix)] - Host::Unix(_) => "", + Some(Host::Tcp(host)) => host.as_str(), + _ => "", }; let tls = tls .make_tls_connect(hostname) .map_err(|e| Error::tls(e.into()))?; - // If both host and hostaddr are specified, the value of hostaddr is used to to establish the TCP connection. - let hostaddr = match host { - Host::Tcp(_hostname) => match config.hostaddr.get(i) { - Some(hostaddr) if hostaddr.is_empty() => Host::Tcp(hostaddr.clone()), - _ => host.clone(), - }, - #[cfg(unix)] - Host::Unix(_v) => host.clone(), + // Try to use the value of hostaddr to establish the TCP connection, + // fallback to host if hostaddr is not present. + let addr = match hostaddr { + Some(ipaddr) => Host::Tcp(ipaddr.to_string()), + None => { + if let Some(host) = host { + host.clone() + } else { + // This is unreachable. + return Err(Error::config("both host and hostaddr are empty".into())); + } + } }; - match connect_once(&hostaddr, port, tls, config).await { + match connect_once(&addr, port, tls, config).await { Ok((client, connection)) => return Ok((client, connection)), Err(e) => error = Some(e), } diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 0ab4a7bab..387c90d7c 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -147,6 +147,58 @@ async fn scram_password_ok() { connect("user=scram_user password=password dbname=postgres").await; } +#[tokio::test] +async fn host_only_ok() { + let _ = tokio_postgres::connect( + "host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_only_ok() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1 port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_and_host_ok() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1 host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_host_mismatch() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1,127.0.0.2 host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .err() + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_host_both_missing() { + let _ = tokio_postgres::connect( + "port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .err() + .unwrap(); +} + #[tokio::test] async fn pipelined_prepare() { let client = connect("user=postgres").await; From d97bed635ef3fe21a3d9dbef0945e57ab2baf8ba Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sat, 27 Aug 2022 11:55:11 -0700 Subject: [PATCH 021/207] also update postgres --- postgres/src/config.rs | 33 +++++++++++++++++++++++++++++++++ tokio-postgres/src/config.rs | 1 + 2 files changed, 34 insertions(+) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index b541ec846..a754ff91f 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -6,6 +6,7 @@ use crate::connection::Connection; use crate::Client; use log::info; use std::fmt; +use std::net::IpAddr; use std::path::Path; use std::str::FromStr; use std::sync::Arc; @@ -39,6 +40,19 @@ 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. +/// * `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, +/// - or if host specifies an IP address, that value will be used directly. +/// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications +/// with time constraints. However, a host name is required for verify-full SSL certificate verification. +/// Specifically: +/// * If `hostaddr` is specified without `host`, the value for `hostaddr` gives the server network address. +/// The connection attempt will fail if the authentication method requires a host name; +/// * If `host` is specified without `hostaddr`, a host name lookup occurs; +/// * If both `host` and `hostaddr` are specified, the value for `hostaddr` gives the server network address. +/// The value for `host` is ignored unless the authentication method requires it, +/// in which case it will be used as the host name. /// * `port` - The port to connect to. Multiple ports can be specified, separated by commas. The number of ports must be /// either 1, in which case it will be used for all hosts, or the same as the number of hosts. Defaults to 5432 if /// omitted or the empty string. @@ -67,6 +81,10 @@ use tokio_postgres::{Error, Socket}; /// ``` /// /// ```not_rust +/// host=host1,host2,host3 port=1234,,5678 hostaddr=127.0.0.1,127.0.0.2,127.0.0.3 user=postgres target_session_attrs=read-write +/// ``` +/// +/// ```not_rust /// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write /// ``` /// @@ -204,6 +222,7 @@ impl Config { /// /// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix /// systems, a host starting with a `/` is interpreted as a path to a directory containing Unix domain sockets. + /// There must be either no hosts, or the same number of hosts as hostaddrs. pub fn host(&mut self, host: &str) -> &mut Config { self.config.host(host); self @@ -214,6 +233,11 @@ impl Config { self.config.get_hosts() } + /// Gets the hostaddrs that have been added to the configuration with `hostaddr`. + pub fn get_hostaddrs(&self) -> &[IpAddr] { + self.config.get_hostaddrs() + } + /// Adds a Unix socket host to the configuration. /// /// Unlike `host`, this method allows non-UTF8 paths. @@ -226,6 +250,15 @@ impl Config { self } + /// Adds a hostaddr to the configuration. + /// + /// Multiple hostaddrs can be specified by calling this method multiple times, and each will be tried in order. + /// There must be either no hostaddrs, or the same number of hostaddrs as hosts. + pub fn hostaddr(&mut self, hostaddr: IpAddr) -> &mut Config { + self.config.hostaddr(hostaddr); + self + } + /// Adds a port to the configuration. /// /// Multiple ports can be specified by calling this method multiple times. There must either be no ports, in which diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 34accdbe8..923da2985 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -302,6 +302,7 @@ impl Config { /// /// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix /// systems, a host starting with a `/` is interpreted as a path to a directory containing Unix domain sockets. + /// There must be either no hosts, or the same number of hosts as hostaddrs. pub fn host(&mut self, host: &str) -> &mut Config { #[cfg(unix)] { From 1a9c1d4ff3e25b7bef01f05c3e396b2eec1564d9 Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sat, 27 Aug 2022 11:55:47 -0700 Subject: [PATCH 022/207] fmt --- postgres/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index a754ff91f..921566b66 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -83,7 +83,7 @@ use tokio_postgres::{Error, Socket}; /// ```not_rust /// host=host1,host2,host3 port=1234,,5678 hostaddr=127.0.0.1,127.0.0.2,127.0.0.3 user=postgres target_session_attrs=read-write /// ``` -/// +/// /// ```not_rust /// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write /// ``` @@ -236,7 +236,7 @@ impl Config { /// Gets the hostaddrs that have been added to the configuration with `hostaddr`. pub fn get_hostaddrs(&self) -> &[IpAddr] { self.config.get_hostaddrs() - } + } /// Adds a Unix socket host to the configuration. /// From 58149dacf6f4633a3c2b24cda442623bd2abb08d Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sun, 28 Aug 2022 12:09:53 -0700 Subject: [PATCH 023/207] explicitly handle host being None --- tokio-postgres/src/connect.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index ee1dc1c76..63574516c 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -51,14 +51,17 @@ where .copied() .unwrap_or(5432); - // The value of host is always used as the hostname for TLS validation. - // postgres doesn't support TLS over unix sockets, so the choice for Host::Unix variant here doesn't matter + // The value of host is used as the hostname for TLS validation, + // if it's not present, use the value of hostaddr. let hostname = match host { - Some(Host::Tcp(host)) => host.as_str(), - _ => "", + Some(Host::Tcp(host)) => host.clone(), + // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter Some() + #[cfg(unix)] + Some(Host::Unix(_)) => "".to_string(), + None => hostaddr.map_or("".to_string(), |ipaddr| ipaddr.to_string()), }; let tls = tls - .make_tls_connect(hostname) + .make_tls_connect(&hostname) .map_err(|e| Error::tls(e.into()))?; // Try to use the value of hostaddr to establish the TCP connection, From 7a648ad0cb911cb9144c0db441399f3189d28b3b Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Sun, 28 Aug 2022 12:18:36 -0700 Subject: [PATCH 024/207] add negative test --- tokio-postgres/src/config.rs | 6 ++++++ tokio-postgres/src/connect.rs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 923da2985..e5bed8ddf 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -1051,4 +1051,10 @@ mod tests { assert_eq!(1, 1); } + + #[test] + fn test_invalid_hostaddr_parsing() { + let s = "user=pass_user dbname=postgres host=host1 hostaddr=127.0.0 port=26257"; + s.parse::().err().unwrap(); + } } diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 63574516c..888f9cf8a 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -55,7 +55,7 @@ where // if it's not present, use the value of hostaddr. let hostname = match host { Some(Host::Tcp(host)) => host.clone(), - // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter Some() + // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter #[cfg(unix)] Some(Host::Unix(_)) => "".to_string(), None => hostaddr.map_or("".to_string(), |ipaddr| ipaddr.to_string()), From a70a7c36c74bfeaf1e171dc2572fddd30d182179 Mon Sep 17 00:00:00 2001 From: Trung Dinh Date: Tue, 30 Aug 2022 22:10:19 -0700 Subject: [PATCH 025/207] move test to runtime --- tokio-postgres/tests/test/main.rs | 52 ---------------------------- tokio-postgres/tests/test/runtime.rs | 52 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 387c90d7c..0ab4a7bab 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -147,58 +147,6 @@ async fn scram_password_ok() { connect("user=scram_user password=password dbname=postgres").await; } -#[tokio::test] -async fn host_only_ok() { - let _ = tokio_postgres::connect( - "host=localhost port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn hostaddr_only_ok() { - let _ = tokio_postgres::connect( - "hostaddr=127.0.0.1 port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn hostaddr_and_host_ok() { - let _ = tokio_postgres::connect( - "hostaddr=127.0.0.1 host=localhost port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .unwrap(); -} - -#[tokio::test] -async fn hostaddr_host_mismatch() { - let _ = tokio_postgres::connect( - "hostaddr=127.0.0.1,127.0.0.2 host=localhost port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .err() - .unwrap(); -} - -#[tokio::test] -async fn hostaddr_host_both_missing() { - let _ = tokio_postgres::connect( - "port=5433 user=pass_user dbname=postgres password=password", - NoTls, - ) - .await - .err() - .unwrap(); -} - #[tokio::test] async fn pipelined_prepare() { let client = connect("user=postgres").await; diff --git a/tokio-postgres/tests/test/runtime.rs b/tokio-postgres/tests/test/runtime.rs index 67b4ead8a..86c1f0701 100644 --- a/tokio-postgres/tests/test/runtime.rs +++ b/tokio-postgres/tests/test/runtime.rs @@ -66,6 +66,58 @@ async fn target_session_attrs_err() { .unwrap(); } +#[tokio::test] +async fn host_only_ok() { + let _ = tokio_postgres::connect( + "host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_only_ok() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1 port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_and_host_ok() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1 host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_host_mismatch() { + let _ = tokio_postgres::connect( + "hostaddr=127.0.0.1,127.0.0.2 host=localhost port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .err() + .unwrap(); +} + +#[tokio::test] +async fn hostaddr_host_both_missing() { + let _ = tokio_postgres::connect( + "port=5433 user=pass_user dbname=postgres password=password", + NoTls, + ) + .await + .err() + .unwrap(); +} + #[tokio::test] async fn cancel_query() { let client = connect("host=localhost port=5433 user=postgres").await; From d368475b50880b920ffbd73b7d6398b2171db53f Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Fri, 30 Dec 2022 13:47:41 +0200 Subject: [PATCH 026/207] Add flush() to front-end messages The PostgreSQL wire protocol has a "Flush" message, which can be used by the clients for long-lived connections. Add a flush() helper for it. --- postgres-protocol/src/message/frontend.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/postgres-protocol/src/message/frontend.rs b/postgres-protocol/src/message/frontend.rs index 5d0a8ff8c..600f7da48 100644 --- a/postgres-protocol/src/message/frontend.rs +++ b/postgres-protocol/src/message/frontend.rs @@ -271,6 +271,12 @@ where }) } +#[inline] +pub fn flush(buf: &mut BytesMut) { + buf.put_u8(b'H'); + write_body(buf, |_| Ok::<(), io::Error>(())).unwrap(); +} + #[inline] pub fn sync(buf: &mut BytesMut) { buf.put_u8(b'S'); From 8ab8f1a5552545986ba660cf2ad45ae2c8e54160 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 30 Dec 2022 08:43:24 -0500 Subject: [PATCH 027/207] Clippy --- postgres-protocol/src/message/backend.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-protocol/src/message/backend.rs b/postgres-protocol/src/message/backend.rs index 3f5374d64..e0eacbea0 100644 --- a/postgres-protocol/src/message/backend.rs +++ b/postgres-protocol/src/message/backend.rs @@ -582,7 +582,7 @@ impl<'a> FallibleIterator for DataRowRanges<'a> { )); } let base = self.len - self.buf.len(); - self.buf = &self.buf[len as usize..]; + self.buf = &self.buf[len..]; Ok(Some(Some(base..base + len))) } } From 0c056148d0c484def905b16b539fb845e31541d4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 1 Jan 2023 20:33:06 -0500 Subject: [PATCH 028/207] Clarify poll_message docs Closes #975 --- tokio-postgres/src/connection.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tokio-postgres/src/connection.rs b/tokio-postgres/src/connection.rs index 30be4e834..414335955 100644 --- a/tokio-postgres/src/connection.rs +++ b/tokio-postgres/src/connection.rs @@ -302,6 +302,9 @@ where /// /// The server can send notices as well as notifications asynchronously to the client. Applications that wish to /// examine those messages should use this method to drive the connection rather than its `Future` implementation. + /// + /// Return values of `None` or `Some(Err(_))` are "terminal"; callers should not invoke this method again after + /// receiving one of those values. pub fn poll_message( &mut self, cx: &mut Context<'_>, From e38e435665af8dbd55e7d803f019405653f99205 Mon Sep 17 00:00:00 2001 From: Ari Breitkreuz Date: Sun, 8 Jan 2023 12:21:42 +0100 Subject: [PATCH 029/207] add batch_execute to generic client --- tokio-postgres/src/generic_client.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index b2a907558..f05a422cc 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -69,6 +69,10 @@ pub trait GenericClient: private::Sealed { /// Like `Client::transaction`. async fn transaction(&mut self) -> Result, Error>; + /// Like `Client::batch_execute`. + async fn batch_execute(&self, query: &str) -> Result<(), Error>; + + /// Returns a reference to the underlying `Client`. fn client(&self) -> &Client; } @@ -149,6 +153,10 @@ impl GenericClient for Client { self.transaction().await } + async fn batch_execute(&self, query: &str) -> Result<(), Error> { + self.batch_execute(query).await + } + fn client(&self) -> &Client { self } @@ -232,6 +240,10 @@ impl GenericClient for Transaction<'_> { self.transaction().await } + async fn batch_execute(&self, query: &str) -> Result<(), Error> { + self.batch_execute(query).await + } + fn client(&self) -> &Client { self.client() } From 383fd50f75a0314cde53b46ec9aafcba870fadbe Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 8 Jan 2023 08:32:00 -0500 Subject: [PATCH 030/207] Update tokio-postgres/src/generic_client.rs --- tokio-postgres/src/generic_client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index f05a422cc..50cff9712 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -72,7 +72,6 @@ pub trait GenericClient: private::Sealed { /// Like `Client::batch_execute`. async fn batch_execute(&self, query: &str) -> Result<(), Error>; - /// Returns a reference to the underlying `Client`. fn client(&self) -> &Client; } From c5cb28bcaf2f4031a88c044b40750a65391d8be8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jan 2023 13:06:33 +0000 Subject: [PATCH 031/207] Update base64 requirement from 0.20 to 0.21 Updates the requirements on [base64](https://github.com/marshallpierce/rust-base64) to permit the latest version. - [Release notes](https://github.com/marshallpierce/rust-base64/releases) - [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md) - [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.20.0...v0.21.0) --- updated-dependencies: - dependency-name: base64 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- postgres-protocol/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index 2a72cc60c..922e92313 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" [dependencies] -base64 = "0.20" +base64 = "0.21" byteorder = "1.0" bytes = "1.0" fallible-iterator = "0.2" From 10bf27128dc4444c650bb371c835a9b87cea4102 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 9 Jan 2023 20:41:16 -0500 Subject: [PATCH 032/207] fix build --- postgres-protocol/src/authentication/sasl.rs | 16 ++++++++++++---- postgres-protocol/src/password/mod.rs | 8 +++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/postgres-protocol/src/authentication/sasl.rs b/postgres-protocol/src/authentication/sasl.rs index ea2f55cad..2352a66c4 100644 --- a/postgres-protocol/src/authentication/sasl.rs +++ b/postgres-protocol/src/authentication/sasl.rs @@ -1,5 +1,8 @@ //! SASL-based authentication support. +use base64::display::Base64Display; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; use hmac::{Hmac, Mac}; use rand::{self, Rng}; use sha2::digest::FixedOutput; @@ -189,7 +192,7 @@ impl ScramSha256 { return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid nonce")); } - let salt = match base64::decode(parsed.salt) { + let salt = match STANDARD.decode(parsed.salt) { Ok(salt) => salt, Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidInput, e)), }; @@ -208,7 +211,7 @@ impl ScramSha256 { let mut cbind_input = vec![]; cbind_input.extend(channel_binding.gs2_header().as_bytes()); cbind_input.extend(channel_binding.cbind_data()); - let cbind_input = base64::encode(&cbind_input); + let cbind_input = STANDARD.encode(&cbind_input); self.message.clear(); write!(&mut self.message, "c={},r={}", cbind_input, parsed.nonce).unwrap(); @@ -225,7 +228,12 @@ impl ScramSha256 { *proof ^= signature; } - write!(&mut self.message, ",p={}", base64::encode(&*client_proof)).unwrap(); + write!( + &mut self.message, + ",p={}", + Base64Display::new(&client_proof, &STANDARD) + ) + .unwrap(); self.state = State::Finish { salted_password, @@ -262,7 +270,7 @@ impl ScramSha256 { ServerFinalMessage::Verifier(verifier) => verifier, }; - let verifier = match base64::decode(verifier) { + let verifier = match STANDARD.decode(verifier) { Ok(verifier) => verifier, Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidInput, e)), }; diff --git a/postgres-protocol/src/password/mod.rs b/postgres-protocol/src/password/mod.rs index a60687bbe..f03bb811d 100644 --- a/postgres-protocol/src/password/mod.rs +++ b/postgres-protocol/src/password/mod.rs @@ -7,6 +7,8 @@ //! end up in logs pg_stat displays, etc. use crate::authentication::sasl; +use base64::display::Base64Display; +use base64::engine::general_purpose::STANDARD; use hmac::{Hmac, Mac}; use md5::Md5; use rand::RngCore; @@ -80,9 +82,9 @@ pub(crate) fn scram_sha_256_salt(password: &[u8], salt: [u8; SCRAM_DEFAULT_SALT_ format!( "SCRAM-SHA-256${}:{}${}:{}", SCRAM_DEFAULT_ITERATIONS, - base64::encode(salt), - base64::encode(stored_key), - base64::encode(server_key) + Base64Display::new(&salt, &STANDARD), + Base64Display::new(&stored_key, &STANDARD), + Base64Display::new(&server_key, &STANDARD) ) } From cf77dc06e69a08327f2656fd8e895038d66da12e Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Tue, 10 Jan 2023 11:47:53 +0000 Subject: [PATCH 033/207] futures-util <0.3.14 doesn't have Stream --- postgres/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index bd7c297f3..e3a8663f2 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -38,7 +38,7 @@ with-time-0_3 = ["tokio-postgres/with-time-0_3"] [dependencies] bytes = "1.0" fallible-iterator = "0.2" -futures-util = { version = "0.3", features = ["sink"] } +futures-util = { version = "0.3.14", features = ["sink"] } tokio-postgres = { version = "0.7.7", path = "../tokio-postgres" } tokio = { version = "1.0", features = ["rt", "time"] } From 205af89aafb8fec5e8a75599e0401d5797932f13 Mon Sep 17 00:00:00 2001 From: Alex Chi Date: Sun, 15 Jan 2023 21:16:56 -0500 Subject: [PATCH 034/207] feat: add `rows_affected` to RowStream Signed-off-by: Alex Chi --- tokio-postgres/src/copy_in.rs | 10 ++----- tokio-postgres/src/query.rs | 43 +++++++++++++++++++++--------- tokio-postgres/src/simple_query.rs | 10 ++----- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/tokio-postgres/src/copy_in.rs b/tokio-postgres/src/copy_in.rs index de1da933b..59e31fea6 100644 --- a/tokio-postgres/src/copy_in.rs +++ b/tokio-postgres/src/copy_in.rs @@ -1,6 +1,7 @@ use crate::client::{InnerClient, Responses}; use crate::codec::FrontendMessage; use crate::connection::RequestMessages; +use crate::query::extract_row_affected; use crate::{query, slice_iter, Error, Statement}; use bytes::{Buf, BufMut, BytesMut}; use futures_channel::mpsc; @@ -110,14 +111,7 @@ where let this = self.as_mut().project(); match ready!(this.responses.poll_next(cx))? { Message::CommandComplete(body) => { - let rows = body - .tag() - .map_err(Error::parse)? - .rsplit(' ') - .next() - .unwrap() - .parse() - .unwrap_or(0); + let rows = extract_row_affected(&body)?; return Poll::Ready(Ok(rows)); } _ => return Poll::Ready(Err(Error::unexpected_message())), diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index 71db8769a..72e92ccda 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -7,7 +7,7 @@ use bytes::{Bytes, BytesMut}; use futures_util::{ready, Stream}; use log::{debug, log_enabled, Level}; use pin_project_lite::pin_project; -use postgres_protocol::message::backend::Message; +use postgres_protocol::message::backend::{CommandCompleteBody, Message}; use postgres_protocol::message::frontend; use std::fmt; use std::marker::PhantomPinned; @@ -52,6 +52,7 @@ where Ok(RowStream { statement, responses, + rows_affected: None, _p: PhantomPinned, }) } @@ -72,10 +73,24 @@ pub async fn query_portal( Ok(RowStream { statement: portal.statement().clone(), responses, + rows_affected: None, _p: PhantomPinned, }) } +/// Extract the number of rows affected from [`CommandCompleteBody`]. +pub fn extract_row_affected(body: &CommandCompleteBody) -> Result { + let rows = body + .tag() + .map_err(Error::parse)? + .rsplit(' ') + .next() + .unwrap() + .parse() + .unwrap_or(0); + Ok(rows) +} + pub async fn execute( client: &InnerClient, statement: Statement, @@ -104,14 +119,7 @@ where match responses.next().await? { Message::DataRow(_) => {} Message::CommandComplete(body) => { - rows = body - .tag() - .map_err(Error::parse)? - .rsplit(' ') - .next() - .unwrap() - .parse() - .unwrap_or(0); + rows = extract_row_affected(&body)?; } Message::EmptyQueryResponse => rows = 0, Message::ReadyForQuery(_) => return Ok(rows), @@ -202,6 +210,7 @@ pin_project! { pub struct RowStream { statement: Statement, responses: Responses, + rows_affected: Option, #[pin] _p: PhantomPinned, } @@ -217,12 +226,22 @@ impl Stream for RowStream { Message::DataRow(body) => { return Poll::Ready(Some(Ok(Row::new(this.statement.clone(), body)?))) } - Message::EmptyQueryResponse - | Message::CommandComplete(_) - | Message::PortalSuspended => {} + Message::CommandComplete(body) => { + *this.rows_affected = Some(extract_row_affected(&body)?); + } + Message::EmptyQueryResponse | Message::PortalSuspended => {} Message::ReadyForQuery(_) => return Poll::Ready(None), _ => return Poll::Ready(Some(Err(Error::unexpected_message()))), } } } } + +impl RowStream { + /// Returns the number of rows affected by the query. + /// + /// This will be `None` if the information is not available yet. + pub fn rows_affected(&self) -> Option { + self.rows_affected + } +} diff --git a/tokio-postgres/src/simple_query.rs b/tokio-postgres/src/simple_query.rs index 7c266e409..bcc6d928b 100644 --- a/tokio-postgres/src/simple_query.rs +++ b/tokio-postgres/src/simple_query.rs @@ -1,6 +1,7 @@ use crate::client::{InnerClient, Responses}; use crate::codec::FrontendMessage; use crate::connection::RequestMessages; +use crate::query::extract_row_affected; use crate::{Error, SimpleQueryMessage, SimpleQueryRow}; use bytes::Bytes; use fallible_iterator::FallibleIterator; @@ -87,14 +88,7 @@ impl Stream for SimpleQueryStream { loop { match ready!(this.responses.poll_next(cx)?) { Message::CommandComplete(body) => { - let rows = body - .tag() - .map_err(Error::parse)? - .rsplit(' ') - .next() - .unwrap() - .parse() - .unwrap_or(0); + let rows = extract_row_affected(&body)?; return Poll::Ready(Some(Ok(SimpleQueryMessage::CommandComplete(rows)))); } Message::EmptyQueryResponse => { From b1842390c3946947ed43bce998d3480b699786ec Mon Sep 17 00:00:00 2001 From: Alex Chi Date: Mon, 16 Jan 2023 22:54:20 -0500 Subject: [PATCH 035/207] update comments Signed-off-by: Alex Chi --- tokio-postgres/src/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index 72e92ccda..12176353b 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -240,7 +240,7 @@ impl Stream for RowStream { impl RowStream { /// Returns the number of rows affected by the query. /// - /// This will be `None` if the information is not available yet. + /// This function will return `None` until the stream has been exhausted. pub fn rows_affected(&self) -> Option { self.rows_affected } From eb086d30a30bb402daddd29cbd53bfcfc9c00a7f Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 24 Feb 2023 09:56:05 +0800 Subject: [PATCH 036/207] export Bytes type through DataRowBody::storage_bytes method. --- postgres-protocol/src/message/backend.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/postgres-protocol/src/message/backend.rs b/postgres-protocol/src/message/backend.rs index e0eacbea0..2b245101e 100644 --- a/postgres-protocol/src/message/backend.rs +++ b/postgres-protocol/src/message/backend.rs @@ -544,6 +544,11 @@ impl DataRowBody { pub fn buffer(&self) -> &[u8] { &self.storage } + + #[inline] + pub fn storage_bytes(&self) -> &Bytes { + &self.storage + } } pub struct DataRowRanges<'a> { From 842eacefc82e1e2d8ef69d60eb14c5e421c5298b Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 25 Feb 2023 10:10:04 -0500 Subject: [PATCH 037/207] fix ci --- .github/workflows/ci.yml | 2 +- postgres-derive-test/src/lib.rs | 4 ++-- postgres-protocol/src/authentication/sasl.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d17f4d6b..92afbb0c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,7 @@ jobs: - run: docker compose up -d - uses: sfackler/actions/rustup@master with: - version: 1.62.0 + version: 1.63.0 - run: echo "::set-output name=version::$(rustc --version)" id: rust-version - uses: actions/cache@v1 diff --git a/postgres-derive-test/src/lib.rs b/postgres-derive-test/src/lib.rs index d1478ac4c..f0534f32c 100644 --- a/postgres-derive-test/src/lib.rs +++ b/postgres-derive-test/src/lib.rs @@ -14,7 +14,7 @@ where T: PartialEq + FromSqlOwned + ToSql + Sync, S: fmt::Display, { - for &(ref val, ref repr) in checks.iter() { + for (val, repr) in checks.iter() { let stmt = conn .prepare(&format!("SELECT {}::{}", *repr, sql_type)) .unwrap(); @@ -38,7 +38,7 @@ pub fn test_type_asymmetric( S: fmt::Display, C: Fn(&T, &F) -> bool, { - for &(ref val, ref repr) in checks.iter() { + for (val, repr) in checks.iter() { let stmt = conn .prepare(&format!("SELECT {}::{}", *repr, sql_type)) .unwrap(); diff --git a/postgres-protocol/src/authentication/sasl.rs b/postgres-protocol/src/authentication/sasl.rs index 2352a66c4..4a77507e9 100644 --- a/postgres-protocol/src/authentication/sasl.rs +++ b/postgres-protocol/src/authentication/sasl.rs @@ -359,7 +359,7 @@ impl<'a> Parser<'a> { } fn posit_number(&mut self) -> io::Result { - let n = self.take_while(|c| matches!(c, '0'..='9'))?; + let n = self.take_while(|c| c.is_ascii_digit())?; n.parse() .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)) } From 63c09eb083ac3a2c3e39c798cda49d35938fefc4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 25 Feb 2023 10:18:38 -0500 Subject: [PATCH 038/207] fix workflow deprecations --- .github/workflows/ci.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92afbb0c8..f81bdee46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: name: rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: sfackler/actions/rustup@master - uses: sfackler/actions/rustfmt@master @@ -25,23 +25,23 @@ jobs: name: clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: sfackler/actions/rustup@master - - run: echo "::set-output name=version::$(rustc --version)" + - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - - uses: actions/cache@v1 + - uses: actions/cache@v3 with: path: ~/.cargo/registry/index key: index-${{ runner.os }}-${{ github.run_number }} restore-keys: | index-${{ runner.os }}- - run: cargo generate-lockfile - - uses: actions/cache@v1 + - uses: actions/cache@v3 with: path: ~/.cargo/registry/cache key: registry-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }} - run: cargo fetch - - uses: actions/cache@v1 + - uses: actions/cache@v3 with: path: target key: clippy-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}y @@ -51,26 +51,26 @@ jobs: name: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: docker compose up -d - uses: sfackler/actions/rustup@master with: version: 1.63.0 - - run: echo "::set-output name=version::$(rustc --version)" + - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - - uses: actions/cache@v1 + - uses: actions/cache@v3 with: path: ~/.cargo/registry/index key: index-${{ runner.os }}-${{ github.run_number }} restore-keys: | index-${{ runner.os }}- - run: cargo generate-lockfile - - uses: actions/cache@v1 + - uses: actions/cache@v3 with: path: ~/.cargo/registry/cache key: registry-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }} - run: cargo fetch - - uses: actions/cache@v1 + - uses: actions/cache@v3 with: path: target key: test-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}y From 7c7c80dcaab7051bce4f31c36abe78c139f08050 Mon Sep 17 00:00:00 2001 From: Benjamin Swart Date: Thu, 23 Feb 2023 18:32:15 +0100 Subject: [PATCH 039/207] Make incorrect number of paramaters an error --- postgres/src/client.rs | 20 -------------------- tokio-postgres/src/client.rs | 32 -------------------------------- tokio-postgres/src/error/mod.rs | 8 ++++++++ tokio-postgres/src/query.rs | 9 +++------ 4 files changed, 11 insertions(+), 58 deletions(-) diff --git a/postgres/src/client.rs b/postgres/src/client.rs index 29cac840d..c8e14cf81 100644 --- a/postgres/src/client.rs +++ b/postgres/src/client.rs @@ -57,10 +57,6 @@ impl Client { /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. - /// /// # Example /// /// ```no_run @@ -96,10 +92,6 @@ impl Client { /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. - /// /// # Examples /// /// ```no_run @@ -134,10 +126,6 @@ impl Client { /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. - /// /// # Examples /// /// ```no_run @@ -172,10 +160,6 @@ impl Client { /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. - /// /// # Examples /// /// ```no_run @@ -213,10 +197,6 @@ impl Client { /// It takes an iterator of parameters rather than a slice, and returns an iterator of rows rather than collecting /// them into an array. /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. - /// /// # Examples /// /// ```no_run diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index ad5aa2866..5d0d2c536 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -230,10 +230,6 @@ impl Client { /// The `statement` argument can either be a `Statement`, or a raw query string. If the same statement will be /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. - /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. pub async fn query( &self, statement: &T, @@ -258,10 +254,6 @@ impl Client { /// The `statement` argument can either be a `Statement`, or a raw query string. If the same statement will be /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. - /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. pub async fn query_one( &self, statement: &T, @@ -295,10 +287,6 @@ impl Client { /// The `statement` argument can either be a `Statement`, or a raw query string. If the same statement will be /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. - /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. pub async fn query_opt( &self, statement: &T, @@ -331,10 +319,6 @@ impl Client { /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. - /// /// [`query`]: #method.query /// /// # Examples @@ -382,10 +366,6 @@ impl Client { /// with the `prepare` method. /// /// If the statement does not modify any rows (e.g. `SELECT`), 0 is returned. - /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. pub async fn execute( &self, statement: &T, @@ -406,10 +386,6 @@ impl Client { /// repeatedly executed (perhaps with different query parameters), consider preparing the statement up front /// with the `prepare` method. /// - /// # Panics - /// - /// Panics if the number of parameters provided does not match the number expected. - /// /// [`execute`]: #method.execute pub async fn execute_raw(&self, statement: &T, params: I) -> Result where @@ -426,10 +402,6 @@ impl Client { /// /// PostgreSQL does not support parameters in `COPY` statements, so this method does not take any. The copy *must* /// be explicitly completed via the `Sink::close` or `finish` methods. If it is not, the copy will be aborted. - /// - /// # Panics - /// - /// Panics if the statement contains parameters. pub async fn copy_in(&self, statement: &T) -> Result, Error> where T: ?Sized + ToStatement, @@ -442,10 +414,6 @@ impl Client { /// Executes a `COPY TO STDOUT` statement, returning a stream of the resulting data. /// /// PostgreSQL does not support parameters in `COPY` statements, so this method does not take any. - /// - /// # Panics - /// - /// Panics if the statement contains parameters. pub async fn copy_out(&self, statement: &T) -> Result where T: ?Sized + ToStatement, diff --git a/tokio-postgres/src/error/mod.rs b/tokio-postgres/src/error/mod.rs index 47a31e793..f1e2644c6 100644 --- a/tokio-postgres/src/error/mod.rs +++ b/tokio-postgres/src/error/mod.rs @@ -344,6 +344,7 @@ enum Kind { ToSql(usize), FromSql(usize), Column(String), + Parameters(usize, usize), Closed, Db, Parse, @@ -383,6 +384,9 @@ impl fmt::Display for Error { Kind::ToSql(idx) => write!(fmt, "error serializing parameter {}", idx)?, Kind::FromSql(idx) => write!(fmt, "error deserializing column {}", idx)?, Kind::Column(column) => write!(fmt, "invalid column `{}`", column)?, + Kind::Parameters(real, expected) => { + write!(fmt, "expected {expected} parameters but got {real}")? + } Kind::Closed => fmt.write_str("connection closed")?, Kind::Db => fmt.write_str("db error")?, Kind::Parse => fmt.write_str("error parsing response from server")?, @@ -474,6 +478,10 @@ impl Error { Error::new(Kind::Column(column), None) } + pub(crate) fn parameters(real: usize, expected: usize) -> Error { + Error::new(Kind::Parameters(real, expected), None) + } + pub(crate) fn tls(e: Box) -> Error { Error::new(Kind::Tls, Some(e)) } diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index 12176353b..e6e1d00a8 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -167,12 +167,9 @@ where let param_types = statement.params(); let params = params.into_iter(); - assert!( - param_types.len() == params.len(), - "expected {} parameters but got {}", - param_types.len(), - params.len() - ); + if param_types.len() != params.len() { + return Err(Error::parameters(params.len(), param_types.len())); + } let (param_formats, params): (Vec<_>, Vec<_>) = params .zip(param_types.iter()) From 7cd7b187a5cb990ceb0ea9531cd3345b1e2799c3 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 26 Feb 2023 16:44:25 -0500 Subject: [PATCH 040/207] Rename accessor --- postgres-protocol/src/message/backend.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-protocol/src/message/backend.rs b/postgres-protocol/src/message/backend.rs index 2b245101e..1b5be1098 100644 --- a/postgres-protocol/src/message/backend.rs +++ b/postgres-protocol/src/message/backend.rs @@ -546,7 +546,7 @@ impl DataRowBody { } #[inline] - pub fn storage_bytes(&self) -> &Bytes { + pub fn buffer_bytes(&self) -> &Bytes { &self.storage } } From 1e1f90786d5df3ac9701b667f9a57bf783357455 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 14:06:03 +0000 Subject: [PATCH 041/207] Update syn requirement from 1.0 to 2.0 Updates the requirements on [syn](https://github.com/dtolnay/syn) to permit the latest version. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/1.0.0...2.0.3) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- postgres-derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-derive/Cargo.toml b/postgres-derive/Cargo.toml index 22b50b707..8470bc8a9 100644 --- a/postgres-derive/Cargo.toml +++ b/postgres-derive/Cargo.toml @@ -12,6 +12,6 @@ proc-macro = true test = false [dependencies] -syn = "1.0" +syn = "2.0" proc-macro2 = "1.0" quote = "1.0" From e9b5a04a4f1f13d8ecb83211132618b5de6af748 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 20 Mar 2023 20:01:56 -0400 Subject: [PATCH 042/207] Fix build --- postgres-derive/src/fromsql.rs | 12 ++++++------ postgres-derive/src/overrides.rs | 21 ++++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/postgres-derive/src/fromsql.rs b/postgres-derive/src/fromsql.rs index f458c6e3d..bb87ded5f 100644 --- a/postgres-derive/src/fromsql.rs +++ b/postgres-derive/src/fromsql.rs @@ -3,10 +3,10 @@ use quote::{format_ident, quote}; use std::iter; use syn::{ punctuated::Punctuated, token, AngleBracketedGenericArguments, Data, DataStruct, DeriveInput, - Error, Fields, GenericArgument, GenericParam, Generics, Ident, Lifetime, LifetimeDef, - PathArguments, PathSegment, + Error, Fields, GenericArgument, GenericParam, Generics, Ident, Lifetime, PathArguments, + PathSegment, }; -use syn::{TraitBound, TraitBoundModifier, TypeParamBound}; +use syn::{LifetimeParam, TraitBound, TraitBoundModifier, TypeParamBound}; use crate::accepts; use crate::composites::Field; @@ -96,9 +96,9 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result { let (impl_generics, _, _) = generics.split_for_impl(); let (_, ty_generics, where_clause) = input.generics.split_for_impl(); let out = quote! { - impl#impl_generics postgres_types::FromSql<#lifetime> for #ident#ty_generics #where_clause { + impl #impl_generics postgres_types::FromSql<#lifetime> for #ident #ty_generics #where_clause { fn from_sql(_type: &postgres_types::Type, buf: &#lifetime [u8]) - -> std::result::Result<#ident#ty_generics, + -> std::result::Result<#ident #ty_generics, std::boxed::Box> { @@ -217,7 +217,7 @@ fn build_generics(source: &Generics) -> (Generics, Lifetime) { let mut out = append_generic_bound(source.to_owned(), &new_fromsql_bound(&lifetime)); out.params.insert( 0, - GenericParam::Lifetime(LifetimeDef::new(lifetime.to_owned())), + GenericParam::Lifetime(LifetimeParam::new(lifetime.to_owned())), ); (out, lifetime) diff --git a/postgres-derive/src/overrides.rs b/postgres-derive/src/overrides.rs index c00d5a94b..ddb37688b 100644 --- a/postgres-derive/src/overrides.rs +++ b/postgres-derive/src/overrides.rs @@ -1,4 +1,5 @@ -use syn::{Attribute, Error, Lit, Meta, NestedMeta}; +use syn::punctuated::Punctuated; +use syn::{Attribute, Error, Expr, ExprLit, Lit, Meta, Token}; pub struct Overrides { pub name: Option, @@ -13,26 +14,28 @@ impl Overrides { }; for attr in attrs { - let attr = attr.parse_meta()?; - if !attr.path().is_ident("postgres") { continue; } - let list = match attr { + let list = match &attr.meta { Meta::List(ref list) => list, bad => return Err(Error::new_spanned(bad, "expected a #[postgres(...)]")), }; - for item in &list.nested { + let nested = list.parse_args_with(Punctuated::::parse_terminated)?; + + for item in nested { match item { - NestedMeta::Meta(Meta::NameValue(meta)) => { + Meta::NameValue(meta) => { if !meta.path.is_ident("name") { return Err(Error::new_spanned(&meta.path, "unknown override")); } - let value = match &meta.lit { - Lit::Str(s) => s.value(), + let value = match &meta.value { + Expr::Lit(ExprLit { + lit: Lit::Str(lit), .. + }) => lit.value(), bad => { return Err(Error::new_spanned(bad, "expected a string literal")) } @@ -40,7 +43,7 @@ impl Overrides { overrides.name = Some(value); } - NestedMeta::Meta(Meta::Path(ref path)) => { + Meta::Path(path) => { if !path.is_ident("transparent") { return Err(Error::new_spanned(path, "unknown override")); } From b4c05f4f81152f430bb196a5b1956e1e36a6fcff Mon Sep 17 00:00:00 2001 From: Paul Hemberger Date: Fri, 24 Mar 2023 11:46:20 -0400 Subject: [PATCH 043/207] wire through knob for TCP user timeout --- tokio-postgres/Cargo.toml | 2 +- tokio-postgres/src/cancel_query.rs | 1 + tokio-postgres/src/client.rs | 1 + tokio-postgres/src/config.rs | 14 ++++++++++++++ tokio-postgres/src/connect.rs | 2 ++ tokio-postgres/src/connect_socket.rs | 12 +++++++++++- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 68737f738..807698f88 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -55,7 +55,7 @@ pin-project-lite = "0.2" phf = "0.11" postgres-protocol = { version = "0.6.4", path = "../postgres-protocol" } postgres-types = { version = "0.2.4", path = "../postgres-types" } -socket2 = "0.4" +socket2 = { version = "0.4", features = ["all"] } tokio = { version = "1.0", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } diff --git a/tokio-postgres/src/cancel_query.rs b/tokio-postgres/src/cancel_query.rs index b02729f85..022278804 100644 --- a/tokio-postgres/src/cancel_query.rs +++ b/tokio-postgres/src/cancel_query.rs @@ -38,6 +38,7 @@ where &config.host, config.port, config.connect_timeout, + config.user_timeout, config.keepalive.as_ref(), ) .await?; diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index 5d0d2c536..9760ee55b 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -156,6 +156,7 @@ pub(crate) struct SocketConfig { pub host: Host, pub port: u16, pub connect_timeout: Option, + pub user_timeout: Option, pub keepalive: Option, } diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 5b364ec06..5930fbd9e 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -160,6 +160,7 @@ pub struct Config { pub(crate) host: Vec, pub(crate) port: Vec, pub(crate) connect_timeout: Option, + pub(crate) user_timeout: Option, pub(crate) keepalives: bool, pub(crate) keepalive_config: KeepaliveConfig, pub(crate) target_session_attrs: TargetSessionAttrs, @@ -190,6 +191,7 @@ impl Config { host: vec![], port: vec![], connect_timeout: None, + user_timeout: None, keepalives: true, keepalive_config, target_session_attrs: TargetSessionAttrs::Any, @@ -340,6 +342,18 @@ impl Config { self.connect_timeout.as_ref() } + /// Sets the TCP user timeout. + pub fn user_timeout(&mut self, user_timeout: Duration) -> &mut Config { + self.user_timeout = Some(user_timeout); + self + } + + /// Gets the TCP user timeout, if one has been set with the + /// `user_timeout` method. + pub fn get_user_timeout(&self) -> Option<&Duration> { + self.user_timeout.as_ref() + } + /// Controls the use of TCP keepalive. /// /// This is ignored for Unix domain socket connections. Defaults to `true`. diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 97a00c812..0006be154 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -65,6 +65,7 @@ where host, port, config.connect_timeout, + config.user_timeout, if config.keepalives { Some(&config.keepalive_config) } else { @@ -118,6 +119,7 @@ where host: host.clone(), port, connect_timeout: config.connect_timeout, + user_timeout: config.user_timeout, keepalive: if config.keepalives { Some(config.keepalive_config.clone()) } else { diff --git a/tokio-postgres/src/connect_socket.rs b/tokio-postgres/src/connect_socket.rs index 19d01d87a..3380ccae9 100644 --- a/tokio-postgres/src/connect_socket.rs +++ b/tokio-postgres/src/connect_socket.rs @@ -14,6 +14,7 @@ pub(crate) async fn connect_socket( host: &Host, port: u16, connect_timeout: Option, + user_timeout: Option, keepalive_config: Option<&KeepaliveConfig>, ) -> Result { match host { @@ -35,8 +36,17 @@ pub(crate) async fn connect_socket( }; stream.set_nodelay(true).map_err(Error::connect)?; + + let sock_ref = SockRef::from(&stream); + #[cfg(target_os = "linux")] + { + sock_ref + .set_tcp_user_timeout(user_timeout) + .map_err(Error::timeout)?; + } + if let Some(keepalive_config) = keepalive_config { - SockRef::from(&stream) + sock_ref .set_tcp_keepalive(&TcpKeepalive::from(keepalive_config)) .map_err(Error::connect)?; } From ee5a139028f9f470f4adae61d80e0404fe310ab2 Mon Sep 17 00:00:00 2001 From: Paul Hemberger Date: Fri, 24 Mar 2023 13:11:18 -0400 Subject: [PATCH 044/207] add in to param parsing; update doc --- tokio-postgres/src/cancel_query.rs | 2 +- tokio-postgres/src/client.rs | 2 +- tokio-postgres/src/config.rs | 28 ++++++++++++++++++++++------ tokio-postgres/src/connect.rs | 4 ++-- tokio-postgres/src/connect_socket.rs | 4 ++-- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/tokio-postgres/src/cancel_query.rs b/tokio-postgres/src/cancel_query.rs index 022278804..d869b5824 100644 --- a/tokio-postgres/src/cancel_query.rs +++ b/tokio-postgres/src/cancel_query.rs @@ -38,7 +38,7 @@ where &config.host, config.port, config.connect_timeout, - config.user_timeout, + config.tcp_user_timeout, config.keepalive.as_ref(), ) .await?; diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index 9760ee55b..8b7df4e87 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -156,7 +156,7 @@ pub(crate) struct SocketConfig { pub host: Host, pub port: u16, pub connect_timeout: Option, - pub user_timeout: Option, + pub tcp_user_timeout: Option, pub keepalive: Option, } diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 5930fbd9e..fd848153f 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -96,6 +96,9 @@ pub enum Host { /// omitted or the empty string. /// * `connect_timeout` - The time limit in seconds applied to each socket-level connection attempt. Note that hostnames /// can resolve to multiple IP addresses, and this limit is applied to each address. Defaults to no timeout. +/// * `tcp_user_timeout` - The time limit that transmitted data may remain unacknowledged before a connection is forcibly closed. +/// This is ignored for Unix domain socket connections. It is only supported on systems where TCP_USER_TIMEOUT is available +/// and will default to the system default; on other systems, it has no effect. /// * `keepalives` - Controls the use of TCP keepalive. A value of 0 disables keepalive and nonzero integers enable it. /// This option is ignored when connecting with Unix sockets. Defaults to on. /// * `keepalives_idle` - The number of seconds of inactivity after which a keepalive message is sent to the server. @@ -160,7 +163,7 @@ pub struct Config { pub(crate) host: Vec, pub(crate) port: Vec, pub(crate) connect_timeout: Option, - pub(crate) user_timeout: Option, + pub(crate) tcp_user_timeout: Option, pub(crate) keepalives: bool, pub(crate) keepalive_config: KeepaliveConfig, pub(crate) target_session_attrs: TargetSessionAttrs, @@ -191,7 +194,7 @@ impl Config { host: vec![], port: vec![], connect_timeout: None, - user_timeout: None, + tcp_user_timeout: None, keepalives: true, keepalive_config, target_session_attrs: TargetSessionAttrs::Any, @@ -343,15 +346,19 @@ impl Config { } /// Sets the TCP user timeout. - pub fn user_timeout(&mut self, user_timeout: Duration) -> &mut Config { - self.user_timeout = Some(user_timeout); + /// + /// This is ignored for Unix domain socket connections. It is only supported on systems where + /// TCP_USER_TIMEOUT is available and will default to the system default; on other systems, + /// it has no effect. + pub fn tcp_user_timeout(&mut self, tcp_user_timeout: Duration) -> &mut Config { + self.tcp_user_timeout = Some(tcp_user_timeout); self } /// Gets the TCP user timeout, if one has been set with the /// `user_timeout` method. - pub fn get_user_timeout(&self) -> Option<&Duration> { - self.user_timeout.as_ref() + pub fn get_tcp_user_timeout(&self) -> Option<&Duration> { + self.tcp_user_timeout.as_ref() } /// Controls the use of TCP keepalive. @@ -488,6 +495,14 @@ impl Config { self.connect_timeout(Duration::from_secs(timeout as u64)); } } + "tcp_user_timeout" => { + let timeout = value + .parse::() + .map_err(|_| Error::config_parse(Box::new(InvalidValue("tcp_user_timeout"))))?; + if timeout > 0 { + self.tcp_user_timeout(Duration::from_secs(timeout as u64)); + } + } "keepalives" => { let keepalives = value .parse::() @@ -609,6 +624,7 @@ impl fmt::Debug for Config { .field("host", &self.host) .field("port", &self.port) .field("connect_timeout", &self.connect_timeout) + .field("tcp_user_timeout", &self.tcp_user_timeout) .field("keepalives", &self.keepalives) .field("keepalives_idle", &self.keepalive_config.idle) .field("keepalives_interval", &self.keepalive_config.interval) diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 0006be154..ed7ecac66 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -65,7 +65,7 @@ where host, port, config.connect_timeout, - config.user_timeout, + config.tcp_user_timeout, if config.keepalives { Some(&config.keepalive_config) } else { @@ -119,7 +119,7 @@ where host: host.clone(), port, connect_timeout: config.connect_timeout, - user_timeout: config.user_timeout, + tcp_user_timeout: config.tcp_user_timeout, keepalive: if config.keepalives { Some(config.keepalive_config.clone()) } else { diff --git a/tokio-postgres/src/connect_socket.rs b/tokio-postgres/src/connect_socket.rs index 3380ccae9..7937df280 100644 --- a/tokio-postgres/src/connect_socket.rs +++ b/tokio-postgres/src/connect_socket.rs @@ -14,7 +14,7 @@ pub(crate) async fn connect_socket( host: &Host, port: u16, connect_timeout: Option, - user_timeout: Option, + tcp_user_timeout: Option, keepalive_config: Option<&KeepaliveConfig>, ) -> Result { match host { @@ -41,7 +41,7 @@ pub(crate) async fn connect_socket( #[cfg(target_os = "linux")] { sock_ref - .set_tcp_user_timeout(user_timeout) + .set_tcp_user_timeout(tcp_user_timeout) .map_err(Error::timeout)?; } From 071dfa3f3b217a32b1e2ab3db9e6ab5132f2fcd1 Mon Sep 17 00:00:00 2001 From: jaydenelliott Date: Sun, 26 Mar 2023 20:33:29 +1100 Subject: [PATCH 045/207] added a rename_all container attribute for enums and structs --- postgres-derive-test/src/composites.rs | 43 +++++++ postgres-derive-test/src/enums.rs | 29 +++++ postgres-derive/src/case.rs | 158 +++++++++++++++++++++++++ postgres-derive/src/composites.rs | 26 ++-- postgres-derive/src/enums.rs | 13 +- postgres-derive/src/fromsql.rs | 9 +- postgres-derive/src/lib.rs | 1 + postgres-derive/src/overrides.rs | 32 ++++- postgres-derive/src/tosql.rs | 9 +- 9 files changed, 299 insertions(+), 21 deletions(-) create mode 100644 postgres-derive/src/case.rs diff --git a/postgres-derive-test/src/composites.rs b/postgres-derive-test/src/composites.rs index a1b76345f..50a22790d 100644 --- a/postgres-derive-test/src/composites.rs +++ b/postgres-derive-test/src/composites.rs @@ -89,6 +89,49 @@ fn name_overrides() { ); } +#[test] +fn rename_all_overrides() { + #[derive(FromSql, ToSql, Debug, PartialEq)] + #[postgres(name = "inventory_item", rename_all = "SCREAMING_SNAKE_CASE")] + struct InventoryItem { + name: String, + supplier_id: i32, + #[postgres(name = "Price")] + price: Option, + } + + let mut conn = Client::connect("user=postgres host=localhost port=5433", NoTls).unwrap(); + conn.batch_execute( + "CREATE TYPE pg_temp.inventory_item AS ( + \"NAME\" TEXT, + \"SUPPLIER_ID\" INT, + \"Price\" DOUBLE PRECISION + );", + ) + .unwrap(); + + let item = InventoryItem { + name: "foobar".to_owned(), + supplier_id: 100, + price: Some(15.50), + }; + + let item_null = InventoryItem { + name: "foobar".to_owned(), + supplier_id: 100, + price: None, + }; + + test_type( + &mut conn, + "inventory_item", + &[ + (item, "ROW('foobar', 100, 15.50)"), + (item_null, "ROW('foobar', 100, NULL)"), + ], + ); +} + #[test] fn wrong_name() { #[derive(FromSql, ToSql, Debug, PartialEq)] diff --git a/postgres-derive-test/src/enums.rs b/postgres-derive-test/src/enums.rs index a7039ca05..e44f37616 100644 --- a/postgres-derive-test/src/enums.rs +++ b/postgres-derive-test/src/enums.rs @@ -53,6 +53,35 @@ fn name_overrides() { ); } +#[test] +fn rename_all_overrides() { + #[derive(Debug, ToSql, FromSql, PartialEq)] + #[postgres(name = "mood", rename_all = "snake_case")] + enum Mood { + Sad, + #[postgres(name = "okay")] + Ok, + Happy, + } + + let mut conn = Client::connect("user=postgres host=localhost port=5433", NoTls).unwrap(); + conn.execute( + "CREATE TYPE pg_temp.mood AS ENUM ('sad', 'okay', 'happy')", + &[], + ) + .unwrap(); + + test_type( + &mut conn, + "mood", + &[ + (Mood::Sad, "'sad'"), + (Mood::Ok, "'okay'"), + (Mood::Happy, "'happy'"), + ], + ); +} + #[test] fn wrong_name() { #[derive(Debug, ToSql, FromSql, PartialEq)] diff --git a/postgres-derive/src/case.rs b/postgres-derive/src/case.rs new file mode 100644 index 000000000..b128990c5 --- /dev/null +++ b/postgres-derive/src/case.rs @@ -0,0 +1,158 @@ +#[allow(deprecated, unused_imports)] +use std::ascii::AsciiExt; + +use self::RenameRule::*; + +/// The different possible ways to change case of fields in a struct, or variants in an enum. +#[allow(clippy::enum_variant_names)] +#[derive(Copy, Clone, PartialEq)] +pub enum RenameRule { + /// Rename direct children to "lowercase" style. + LowerCase, + /// Rename direct children to "UPPERCASE" style. + UpperCase, + /// Rename direct children to "PascalCase" style, as typically used for + /// enum variants. + PascalCase, + /// Rename direct children to "camelCase" style. + CamelCase, + /// Rename direct children to "snake_case" style, as commonly used for + /// fields. + SnakeCase, + /// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly + /// used for constants. + ScreamingSnakeCase, + /// Rename direct children to "kebab-case" style. + KebabCase, + /// Rename direct children to "SCREAMING-KEBAB-CASE" style. + ScreamingKebabCase, +} + +pub static RENAME_RULES: &[(&str, RenameRule)] = &[ + ("lowercase", LowerCase), + ("UPPERCASE", UpperCase), + ("PascalCase", PascalCase), + ("camelCase", CamelCase), + ("snake_case", SnakeCase), + ("SCREAMING_SNAKE_CASE", ScreamingSnakeCase), + ("kebab-case", KebabCase), + ("SCREAMING-KEBAB-CASE", ScreamingKebabCase), +]; + +impl RenameRule { + /// Apply a renaming rule to an enum variant, returning the version expected in the source. + pub fn apply_to_variant(&self, variant: &str) -> String { + match *self { + PascalCase => variant.to_owned(), + LowerCase => variant.to_ascii_lowercase(), + UpperCase => variant.to_ascii_uppercase(), + CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..], + SnakeCase => { + let mut snake = String::new(); + for (i, ch) in variant.char_indices() { + if i > 0 && ch.is_uppercase() { + snake.push('_'); + } + snake.push(ch.to_ascii_lowercase()); + } + snake + } + ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(), + KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"), + ScreamingKebabCase => ScreamingSnakeCase + .apply_to_variant(variant) + .replace('_', "-"), + } + } + + /// Apply a renaming rule to a struct field, returning the version expected in the source. + pub fn apply_to_field(&self, field: &str) -> String { + match *self { + LowerCase | SnakeCase => field.to_owned(), + UpperCase => field.to_ascii_uppercase(), + PascalCase => { + let mut pascal = String::new(); + let mut capitalize = true; + for ch in field.chars() { + if ch == '_' { + capitalize = true; + } else if capitalize { + pascal.push(ch.to_ascii_uppercase()); + capitalize = false; + } else { + pascal.push(ch); + } + } + pascal + } + CamelCase => { + let pascal = PascalCase.apply_to_field(field); + pascal[..1].to_ascii_lowercase() + &pascal[1..] + } + ScreamingSnakeCase => field.to_ascii_uppercase(), + KebabCase => field.replace('_', "-"), + ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"), + } + } +} + +#[test] +fn rename_variants() { + for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[ + ( + "Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", + ), + ( + "VeryTasty", + "verytasty", + "VERYTASTY", + "veryTasty", + "very_tasty", + "VERY_TASTY", + "very-tasty", + "VERY-TASTY", + ), + ("A", "a", "A", "a", "a", "A", "a", "A"), + ("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"), + ] { + assert_eq!(LowerCase.apply_to_variant(original), lower); + assert_eq!(UpperCase.apply_to_variant(original), upper); + assert_eq!(PascalCase.apply_to_variant(original), original); + assert_eq!(CamelCase.apply_to_variant(original), camel); + assert_eq!(SnakeCase.apply_to_variant(original), snake); + assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming); + assert_eq!(KebabCase.apply_to_variant(original), kebab); + assert_eq!( + ScreamingKebabCase.apply_to_variant(original), + screaming_kebab + ); + } +} + +#[test] +fn rename_fields() { + for &(original, upper, pascal, camel, screaming, kebab, screaming_kebab) in &[ + ( + "outcome", "OUTCOME", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", + ), + ( + "very_tasty", + "VERY_TASTY", + "VeryTasty", + "veryTasty", + "VERY_TASTY", + "very-tasty", + "VERY-TASTY", + ), + ("a", "A", "A", "a", "A", "a", "A"), + ("z42", "Z42", "Z42", "z42", "Z42", "z42", "Z42"), + ] { + assert_eq!(UpperCase.apply_to_field(original), upper); + assert_eq!(PascalCase.apply_to_field(original), pascal); + assert_eq!(CamelCase.apply_to_field(original), camel); + assert_eq!(SnakeCase.apply_to_field(original), original); + assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming); + assert_eq!(KebabCase.apply_to_field(original), kebab); + assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab); + } +} diff --git a/postgres-derive/src/composites.rs b/postgres-derive/src/composites.rs index 15bfabc13..dcff2c581 100644 --- a/postgres-derive/src/composites.rs +++ b/postgres-derive/src/composites.rs @@ -4,7 +4,7 @@ use syn::{ TypeParamBound, }; -use crate::overrides::Overrides; +use crate::{case::RenameRule, overrides::Overrides}; pub struct Field { pub name: String, @@ -13,18 +13,26 @@ pub struct Field { } impl Field { - pub fn parse(raw: &syn::Field) -> Result { + pub fn parse(raw: &syn::Field, rename_all: Option) -> Result { let overrides = Overrides::extract(&raw.attrs)?; - let ident = raw.ident.as_ref().unwrap().clone(); - Ok(Field { - name: overrides.name.unwrap_or_else(|| { + + // field level name override takes precendence over container level rename_all override + let name = match overrides.name { + Some(n) => n, + None => { let name = ident.to_string(); - match name.strip_prefix("r#") { - Some(name) => name.to_string(), - None => name, + let stripped = name.strip_prefix("r#").map(String::from).unwrap_or(name); + + match rename_all { + Some(rule) => rule.apply_to_field(&stripped), + None => stripped, } - }), + } + }; + + Ok(Field { + name, ident, type_: raw.ty.clone(), }) diff --git a/postgres-derive/src/enums.rs b/postgres-derive/src/enums.rs index 3c6bc7113..d99eca1c4 100644 --- a/postgres-derive/src/enums.rs +++ b/postgres-derive/src/enums.rs @@ -1,6 +1,6 @@ use syn::{Error, Fields, Ident}; -use crate::overrides::Overrides; +use crate::{case::RenameRule, overrides::Overrides}; pub struct Variant { pub ident: Ident, @@ -8,7 +8,7 @@ pub struct Variant { } impl Variant { - pub fn parse(raw: &syn::Variant) -> Result { + pub fn parse(raw: &syn::Variant, rename_all: Option) -> Result { match raw.fields { Fields::Unit => {} _ => { @@ -18,11 +18,16 @@ impl Variant { )) } } - let overrides = Overrides::extract(&raw.attrs)?; + + // variant level name override takes precendence over container level rename_all override + let name = overrides.name.unwrap_or_else(|| match rename_all { + Some(rule) => rule.apply_to_variant(&raw.ident.to_string()), + None => raw.ident.to_string(), + }); Ok(Variant { ident: raw.ident.clone(), - name: overrides.name.unwrap_or_else(|| raw.ident.to_string()), + name, }) } } diff --git a/postgres-derive/src/fromsql.rs b/postgres-derive/src/fromsql.rs index bb87ded5f..3736e01e9 100644 --- a/postgres-derive/src/fromsql.rs +++ b/postgres-derive/src/fromsql.rs @@ -24,7 +24,10 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result { )); } - let name = overrides.name.unwrap_or_else(|| input.ident.to_string()); + let name = overrides + .name + .clone() + .unwrap_or_else(|| input.ident.to_string()); let (accepts_body, to_sql_body) = if overrides.transparent { match input.data { @@ -51,7 +54,7 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result { let variants = data .variants .iter() - .map(Variant::parse) + .map(|variant| Variant::parse(variant, overrides.rename_all)) .collect::, _>>()?; ( accepts::enum_body(&name, &variants), @@ -75,7 +78,7 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result { let fields = fields .named .iter() - .map(Field::parse) + .map(|field| Field::parse(field, overrides.rename_all)) .collect::, _>>()?; ( accepts::composite_body(&name, "FromSql", &fields), diff --git a/postgres-derive/src/lib.rs b/postgres-derive/src/lib.rs index 98e6add24..b849096c9 100644 --- a/postgres-derive/src/lib.rs +++ b/postgres-derive/src/lib.rs @@ -7,6 +7,7 @@ use proc_macro::TokenStream; use syn::parse_macro_input; mod accepts; +mod case; mod composites; mod enums; mod fromsql; diff --git a/postgres-derive/src/overrides.rs b/postgres-derive/src/overrides.rs index ddb37688b..3918446a2 100644 --- a/postgres-derive/src/overrides.rs +++ b/postgres-derive/src/overrides.rs @@ -1,8 +1,11 @@ use syn::punctuated::Punctuated; use syn::{Attribute, Error, Expr, ExprLit, Lit, Meta, Token}; +use crate::case::{RenameRule, RENAME_RULES}; + pub struct Overrides { pub name: Option, + pub rename_all: Option, pub transparent: bool, } @@ -10,6 +13,7 @@ impl Overrides { pub fn extract(attrs: &[Attribute]) -> Result { let mut overrides = Overrides { name: None, + rename_all: None, transparent: false, }; @@ -28,7 +32,9 @@ impl Overrides { for item in nested { match item { Meta::NameValue(meta) => { - if !meta.path.is_ident("name") { + let name_override = meta.path.is_ident("name"); + let rename_all_override = meta.path.is_ident("rename_all"); + if !name_override && !rename_all_override { return Err(Error::new_spanned(&meta.path, "unknown override")); } @@ -41,7 +47,29 @@ impl Overrides { } }; - overrides.name = Some(value); + if name_override { + overrides.name = Some(value); + } else if rename_all_override { + let rename_rule = RENAME_RULES + .iter() + .find(|rule| rule.0 == value) + .map(|val| val.1) + .ok_or_else(|| { + Error::new_spanned( + &meta.value, + format!( + "invalid rename_all rule, expected one of: {}", + RENAME_RULES + .iter() + .map(|rule| format!("\"{}\"", rule.0)) + .collect::>() + .join(", ") + ), + ) + })?; + + overrides.rename_all = Some(rename_rule); + } } Meta::Path(path) => { if !path.is_ident("transparent") { diff --git a/postgres-derive/src/tosql.rs b/postgres-derive/src/tosql.rs index e51acc7fd..1e91df4f6 100644 --- a/postgres-derive/src/tosql.rs +++ b/postgres-derive/src/tosql.rs @@ -22,7 +22,10 @@ pub fn expand_derive_tosql(input: DeriveInput) -> Result { )); } - let name = overrides.name.unwrap_or_else(|| input.ident.to_string()); + let name = overrides + .name + .clone() + .unwrap_or_else(|| input.ident.to_string()); let (accepts_body, to_sql_body) = if overrides.transparent { match input.data { @@ -47,7 +50,7 @@ pub fn expand_derive_tosql(input: DeriveInput) -> Result { let variants = data .variants .iter() - .map(Variant::parse) + .map(|variant| Variant::parse(variant, overrides.rename_all)) .collect::, _>>()?; ( accepts::enum_body(&name, &variants), @@ -69,7 +72,7 @@ pub fn expand_derive_tosql(input: DeriveInput) -> Result { let fields = fields .named .iter() - .map(Field::parse) + .map(|field| Field::parse(field, overrides.rename_all)) .collect::, _>>()?; ( accepts::composite_body(&name, "ToSql", &fields), From bc8ad8aee69f14e367de2f42c8d3a61c1d9c144b Mon Sep 17 00:00:00 2001 From: jaydenelliott Date: Mon, 27 Mar 2023 18:22:53 +1100 Subject: [PATCH 046/207] Distinguish between field and container attributes when parsing --- postgres-derive/src/composites.rs | 2 +- postgres-derive/src/enums.rs | 2 +- postgres-derive/src/fromsql.rs | 2 +- postgres-derive/src/overrides.rs | 8 +++++++- postgres-derive/src/tosql.rs | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/postgres-derive/src/composites.rs b/postgres-derive/src/composites.rs index dcff2c581..b6aad8ab3 100644 --- a/postgres-derive/src/composites.rs +++ b/postgres-derive/src/composites.rs @@ -14,7 +14,7 @@ pub struct Field { impl Field { pub fn parse(raw: &syn::Field, rename_all: Option) -> Result { - let overrides = Overrides::extract(&raw.attrs)?; + let overrides = Overrides::extract(&raw.attrs, false)?; let ident = raw.ident.as_ref().unwrap().clone(); // field level name override takes precendence over container level rename_all override diff --git a/postgres-derive/src/enums.rs b/postgres-derive/src/enums.rs index d99eca1c4..3e4b5045f 100644 --- a/postgres-derive/src/enums.rs +++ b/postgres-derive/src/enums.rs @@ -18,7 +18,7 @@ impl Variant { )) } } - let overrides = Overrides::extract(&raw.attrs)?; + let overrides = Overrides::extract(&raw.attrs, false)?; // variant level name override takes precendence over container level rename_all override let name = overrides.name.unwrap_or_else(|| match rename_all { diff --git a/postgres-derive/src/fromsql.rs b/postgres-derive/src/fromsql.rs index 3736e01e9..4deb23ed2 100644 --- a/postgres-derive/src/fromsql.rs +++ b/postgres-derive/src/fromsql.rs @@ -15,7 +15,7 @@ use crate::enums::Variant; use crate::overrides::Overrides; pub fn expand_derive_fromsql(input: DeriveInput) -> Result { - let overrides = Overrides::extract(&input.attrs)?; + let overrides = Overrides::extract(&input.attrs, true)?; if overrides.name.is_some() && overrides.transparent { return Err(Error::new_spanned( diff --git a/postgres-derive/src/overrides.rs b/postgres-derive/src/overrides.rs index 3918446a2..7f28375bc 100644 --- a/postgres-derive/src/overrides.rs +++ b/postgres-derive/src/overrides.rs @@ -10,7 +10,7 @@ pub struct Overrides { } impl Overrides { - pub fn extract(attrs: &[Attribute]) -> Result { + pub fn extract(attrs: &[Attribute], container_attr: bool) -> Result { let mut overrides = Overrides { name: None, rename_all: None, @@ -34,6 +34,12 @@ impl Overrides { Meta::NameValue(meta) => { let name_override = meta.path.is_ident("name"); let rename_all_override = meta.path.is_ident("rename_all"); + if !container_attr && rename_all_override { + return Err(Error::new_spanned( + &meta.path, + "rename_all is a container attribute", + )); + } if !name_override && !rename_all_override { return Err(Error::new_spanned(&meta.path, "unknown override")); } diff --git a/postgres-derive/src/tosql.rs b/postgres-derive/src/tosql.rs index 1e91df4f6..dbeeb16c3 100644 --- a/postgres-derive/src/tosql.rs +++ b/postgres-derive/src/tosql.rs @@ -13,7 +13,7 @@ use crate::enums::Variant; use crate::overrides::Overrides; pub fn expand_derive_tosql(input: DeriveInput) -> Result { - let overrides = Overrides::extract(&input.attrs)?; + let overrides = Overrides::extract(&input.attrs, true)?; if overrides.name.is_some() && overrides.transparent { return Err(Error::new_spanned( From d509b3bc52df9cf0d7f1f2ac5ac64b0bfc643160 Mon Sep 17 00:00:00 2001 From: jaydenelliott Date: Mon, 27 Mar 2023 18:45:05 +1100 Subject: [PATCH 047/207] Replaced case conversion with heck --- postgres-derive/Cargo.toml | 1 + postgres-derive/src/case.rs | 138 ++++++++++--------------------- postgres-derive/src/enums.rs | 2 +- postgres-derive/src/overrides.rs | 30 +++---- 4 files changed, 60 insertions(+), 111 deletions(-) diff --git a/postgres-derive/Cargo.toml b/postgres-derive/Cargo.toml index 8470bc8a9..cfc8829f4 100644 --- a/postgres-derive/Cargo.toml +++ b/postgres-derive/Cargo.toml @@ -15,3 +15,4 @@ test = false syn = "2.0" proc-macro2 = "1.0" quote = "1.0" +heck = "0.4" \ No newline at end of file diff --git a/postgres-derive/src/case.rs b/postgres-derive/src/case.rs index b128990c5..20ecc8eed 100644 --- a/postgres-derive/src/case.rs +++ b/postgres-derive/src/case.rs @@ -1,6 +1,11 @@ #[allow(deprecated, unused_imports)] use std::ascii::AsciiExt; +use heck::{ + ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTrainCase, + ToUpperCamelCase, +}; + use self::RenameRule::*; /// The different possible ways to change case of fields in a struct, or variants in an enum. @@ -26,78 +31,56 @@ pub enum RenameRule { KebabCase, /// Rename direct children to "SCREAMING-KEBAB-CASE" style. ScreamingKebabCase, + + /// Rename direct children to "Train-Case" style. + TrainCase, } -pub static RENAME_RULES: &[(&str, RenameRule)] = &[ - ("lowercase", LowerCase), - ("UPPERCASE", UpperCase), - ("PascalCase", PascalCase), - ("camelCase", CamelCase), - ("snake_case", SnakeCase), - ("SCREAMING_SNAKE_CASE", ScreamingSnakeCase), - ("kebab-case", KebabCase), - ("SCREAMING-KEBAB-CASE", ScreamingKebabCase), +pub const RENAME_RULES: &[&str] = &[ + "lowercase", + "UPPERCASE", + "PascalCase", + "camelCase", + "snake_case", + "SCREAMING_SNAKE_CASE", + "kebab-case", + "SCREAMING-KEBAB-CASE", + "Train-Case", ]; impl RenameRule { - /// Apply a renaming rule to an enum variant, returning the version expected in the source. - pub fn apply_to_variant(&self, variant: &str) -> String { - match *self { - PascalCase => variant.to_owned(), - LowerCase => variant.to_ascii_lowercase(), - UpperCase => variant.to_ascii_uppercase(), - CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..], - SnakeCase => { - let mut snake = String::new(); - for (i, ch) in variant.char_indices() { - if i > 0 && ch.is_uppercase() { - snake.push('_'); - } - snake.push(ch.to_ascii_lowercase()); - } - snake - } - ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(), - KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"), - ScreamingKebabCase => ScreamingSnakeCase - .apply_to_variant(variant) - .replace('_', "-"), + pub fn from_str(rule: &str) -> Option { + match rule { + "lowercase" => Some(LowerCase), + "UPPERCASE" => Some(UpperCase), + "PascalCase" => Some(PascalCase), + "camelCase" => Some(CamelCase), + "snake_case" => Some(SnakeCase), + "SCREAMING_SNAKE_CASE" => Some(ScreamingSnakeCase), + "kebab-case" => Some(KebabCase), + "SCREAMING-KEBAB-CASE" => Some(ScreamingKebabCase), + "Train-Case" => Some(TrainCase), + _ => None, } } - - /// Apply a renaming rule to a struct field, returning the version expected in the source. - pub fn apply_to_field(&self, field: &str) -> String { + /// Apply a renaming rule to an enum or struct field, returning the version expected in the source. + pub fn apply_to_field(&self, variant: &str) -> String { match *self { - LowerCase | SnakeCase => field.to_owned(), - UpperCase => field.to_ascii_uppercase(), - PascalCase => { - let mut pascal = String::new(); - let mut capitalize = true; - for ch in field.chars() { - if ch == '_' { - capitalize = true; - } else if capitalize { - pascal.push(ch.to_ascii_uppercase()); - capitalize = false; - } else { - pascal.push(ch); - } - } - pascal - } - CamelCase => { - let pascal = PascalCase.apply_to_field(field); - pascal[..1].to_ascii_lowercase() + &pascal[1..] - } - ScreamingSnakeCase => field.to_ascii_uppercase(), - KebabCase => field.replace('_', "-"), - ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"), + LowerCase => variant.to_lowercase(), + UpperCase => variant.to_uppercase(), + PascalCase => variant.to_upper_camel_case(), + CamelCase => variant.to_lower_camel_case(), + SnakeCase => variant.to_snake_case(), + ScreamingSnakeCase => variant.to_shouty_snake_case(), + KebabCase => variant.to_kebab_case(), + ScreamingKebabCase => variant.to_shouty_kebab_case(), + TrainCase => variant.to_train_case(), } } } #[test] -fn rename_variants() { +fn rename_field() { for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[ ( "Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", @@ -115,42 +98,11 @@ fn rename_variants() { ("A", "a", "A", "a", "a", "A", "a", "A"), ("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"), ] { - assert_eq!(LowerCase.apply_to_variant(original), lower); - assert_eq!(UpperCase.apply_to_variant(original), upper); - assert_eq!(PascalCase.apply_to_variant(original), original); - assert_eq!(CamelCase.apply_to_variant(original), camel); - assert_eq!(SnakeCase.apply_to_variant(original), snake); - assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming); - assert_eq!(KebabCase.apply_to_variant(original), kebab); - assert_eq!( - ScreamingKebabCase.apply_to_variant(original), - screaming_kebab - ); - } -} - -#[test] -fn rename_fields() { - for &(original, upper, pascal, camel, screaming, kebab, screaming_kebab) in &[ - ( - "outcome", "OUTCOME", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME", - ), - ( - "very_tasty", - "VERY_TASTY", - "VeryTasty", - "veryTasty", - "VERY_TASTY", - "very-tasty", - "VERY-TASTY", - ), - ("a", "A", "A", "a", "A", "a", "A"), - ("z42", "Z42", "Z42", "z42", "Z42", "z42", "Z42"), - ] { + assert_eq!(LowerCase.apply_to_field(original), lower); assert_eq!(UpperCase.apply_to_field(original), upper); - assert_eq!(PascalCase.apply_to_field(original), pascal); + assert_eq!(PascalCase.apply_to_field(original), original); assert_eq!(CamelCase.apply_to_field(original), camel); - assert_eq!(SnakeCase.apply_to_field(original), original); + assert_eq!(SnakeCase.apply_to_field(original), snake); assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming); assert_eq!(KebabCase.apply_to_field(original), kebab); assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab); diff --git a/postgres-derive/src/enums.rs b/postgres-derive/src/enums.rs index 3e4b5045f..9a6dfa926 100644 --- a/postgres-derive/src/enums.rs +++ b/postgres-derive/src/enums.rs @@ -22,7 +22,7 @@ impl Variant { // variant level name override takes precendence over container level rename_all override let name = overrides.name.unwrap_or_else(|| match rename_all { - Some(rule) => rule.apply_to_variant(&raw.ident.to_string()), + Some(rule) => rule.apply_to_field(&raw.ident.to_string()), None => raw.ident.to_string(), }); Ok(Variant { diff --git a/postgres-derive/src/overrides.rs b/postgres-derive/src/overrides.rs index 7f28375bc..99faeebb7 100644 --- a/postgres-derive/src/overrides.rs +++ b/postgres-derive/src/overrides.rs @@ -56,23 +56,19 @@ impl Overrides { if name_override { overrides.name = Some(value); } else if rename_all_override { - let rename_rule = RENAME_RULES - .iter() - .find(|rule| rule.0 == value) - .map(|val| val.1) - .ok_or_else(|| { - Error::new_spanned( - &meta.value, - format!( - "invalid rename_all rule, expected one of: {}", - RENAME_RULES - .iter() - .map(|rule| format!("\"{}\"", rule.0)) - .collect::>() - .join(", ") - ), - ) - })?; + let rename_rule = RenameRule::from_str(&value).ok_or_else(|| { + Error::new_spanned( + &meta.value, + format!( + "invalid rename_all rule, expected one of: {}", + RENAME_RULES + .iter() + .map(|rule| format!("\"{}\"", rule)) + .collect::>() + .join(", ") + ), + ) + })?; overrides.rename_all = Some(rename_rule); } From a9967c05ff40de34e1471bf28cd956e756d1f6f9 Mon Sep 17 00:00:00 2001 From: Paul Hemberger Date: Mon, 27 Mar 2023 16:47:48 -0400 Subject: [PATCH 048/207] docs: mention sys default if 0 --- tokio-postgres/src/config.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index fd848153f..a8aa7a9f5 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -98,7 +98,7 @@ pub enum Host { /// can resolve to multiple IP addresses, and this limit is applied to each address. Defaults to no timeout. /// * `tcp_user_timeout` - The time limit that transmitted data may remain unacknowledged before a connection is forcibly closed. /// This is ignored for Unix domain socket connections. It is only supported on systems where TCP_USER_TIMEOUT is available -/// and will default to the system default; on other systems, it has no effect. +/// and will default to the system default if omitted or set to 0; on other systems, it has no effect. /// * `keepalives` - Controls the use of TCP keepalive. A value of 0 disables keepalive and nonzero integers enable it. /// This option is ignored when connecting with Unix sockets. Defaults to on. /// * `keepalives_idle` - The number of seconds of inactivity after which a keepalive message is sent to the server. @@ -348,8 +348,8 @@ impl Config { /// Sets the TCP user timeout. /// /// This is ignored for Unix domain socket connections. It is only supported on systems where - /// TCP_USER_TIMEOUT is available and will default to the system default; on other systems, - /// it has no effect. + /// TCP_USER_TIMEOUT is available and will default to the system default if omitted or set to 0; + /// on other systems, it has no effect. pub fn tcp_user_timeout(&mut self, tcp_user_timeout: Duration) -> &mut Config { self.tcp_user_timeout = Some(tcp_user_timeout); self From 62a443222c8f660438251ef17cb2ca088f48e207 Mon Sep 17 00:00:00 2001 From: Paul Hemberger Date: Mon, 27 Mar 2023 16:47:58 -0400 Subject: [PATCH 049/207] use correct error type --- tokio-postgres/src/connect_socket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/src/connect_socket.rs b/tokio-postgres/src/connect_socket.rs index 7937df280..9b3d31d72 100644 --- a/tokio-postgres/src/connect_socket.rs +++ b/tokio-postgres/src/connect_socket.rs @@ -42,7 +42,7 @@ pub(crate) async fn connect_socket( { sock_ref .set_tcp_user_timeout(tcp_user_timeout) - .map_err(Error::timeout)?; + .map_err(Error::connect)?; } if let Some(keepalive_config) = keepalive_config { From 58f06610cc9e0437c7779ce7dad234fa7a2241c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:08:40 +0000 Subject: [PATCH 050/207] Update socket2 requirement from 0.4 to 0.5 Updates the requirements on [socket2](https://github.com/rust-lang/socket2) to permit the latest version. - [Release notes](https://github.com/rust-lang/socket2/releases) - [Changelog](https://github.com/rust-lang/socket2/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/socket2/compare/v0.4.0...v0.5.1) --- updated-dependencies: - dependency-name: socket2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- tokio-postgres/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 807698f88..39e41d85c 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -55,7 +55,7 @@ pin-project-lite = "0.2" phf = "0.11" postgres-protocol = { version = "0.6.4", path = "../postgres-protocol" } postgres-types = { version = "0.2.4", path = "../postgres-types" } -socket2 = { version = "0.4", features = ["all"] } +socket2 = { version = "0.5", features = ["all"] } tokio = { version = "1.0", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } From a205d23141bb389693bf27a7e8233b5db072a7f5 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 18:04:52 -0400 Subject: [PATCH 051/207] Update Cargo.toml --- tokio-postgres/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 39e41d85c..c29852dec 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -56,7 +56,7 @@ phf = "0.11" postgres-protocol = { version = "0.6.4", path = "../postgres-protocol" } postgres-types = { version = "0.2.4", path = "../postgres-types" } socket2 = { version = "0.5", features = ["all"] } -tokio = { version = "1.0", features = ["io-util"] } +tokio = { version = "1.27", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } [dev-dependencies] From 9c0d2dddc43b6137ae5bb0a540b3442b33565fa4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 18:14:05 -0400 Subject: [PATCH 052/207] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f81bdee46..8044b2f47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,7 @@ jobs: - run: docker compose up -d - uses: sfackler/actions/rustup@master with: - version: 1.63.0 + version: 1.64.0 - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - uses: actions/cache@v3 From 5be97d9559ff7077eb77486ca2c789907f58ff9c Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 18:59:29 -0400 Subject: [PATCH 053/207] Release postgres-types v0.2.5 --- postgres-types/CHANGELOG.md | 6 ++++++ postgres-types/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/postgres-types/CHANGELOG.md b/postgres-types/CHANGELOG.md index 0e2167e5f..0f42f3495 100644 --- a/postgres-types/CHANGELOG.md +++ b/postgres-types/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.2.5 - 2023-03-27 + +## Added + +* Added support for multi-range types. + ## v0.2.4 - 2022-08-20 ## Added diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index 70f1ed54a..35cdd6e7b 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-types" -version = "0.2.4" +version = "0.2.5" authors = ["Steven Fackler "] edition = "2018" license = "MIT/Apache-2.0" From a46796798aff0ab30e21abe9160bf1246b354626 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 19:02:39 -0400 Subject: [PATCH 054/207] Release postgres-protocol v0.6.5 --- postgres-protocol/CHANGELOG.md | 11 +++++++++++ postgres-protocol/Cargo.toml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/postgres-protocol/CHANGELOG.md b/postgres-protocol/CHANGELOG.md index d84f29ded..034fd637c 100644 --- a/postgres-protocol/CHANGELOG.md +++ b/postgres-protocol/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +## v0.6.5 - 2023-03-27 + +### Added + +* Added `message::frontend::flush`. +* Added `DataRowBody::buffer_bytes`. + +### Changed + +* Upgraded `base64`. + ## v0.6.4 - 2022-04-03 ### Added diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index 922e92313..e32211369 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-protocol" -version = "0.6.4" +version = "0.6.5" authors = ["Steven Fackler "] edition = "2018" description = "Low level Postgres protocol APIs" From 54390eb3fe8c4766c22cbaba91ffae3885af00e3 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 19:04:38 -0400 Subject: [PATCH 055/207] Release postgres-derive v0.4.4 --- postgres-derive/CHANGELOG.md | 6 ++++++ postgres-derive/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/postgres-derive/CHANGELOG.md b/postgres-derive/CHANGELOG.md index dde466a97..22714acc2 100644 --- a/postgres-derive/CHANGELOG.md +++ b/postgres-derive/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.4.4 - 2023-03-27 + +### Changed + +* Upgraded `syn`. + ## v0.4.3 - 2022-09-07 ### Added diff --git a/postgres-derive/Cargo.toml b/postgres-derive/Cargo.toml index 8470bc8a9..535a64315 100644 --- a/postgres-derive/Cargo.toml +++ b/postgres-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-derive" -version = "0.4.3" +version = "0.4.4" authors = ["Steven Fackler "] license = "MIT/Apache-2.0" edition = "2018" From 117e387a3e44c13068dca613b36037c5de35d65c Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 19:14:49 -0400 Subject: [PATCH 056/207] Release tokio-postgres v0.7.8 --- tokio-postgres/CHANGELOG.md | 16 ++++++++++++++++ tokio-postgres/Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index 91e78b780..3345a1d43 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -1,5 +1,21 @@ # Change Log +## v0.7.8 + +## Added + +* Added `keepalives_interval` and `keepalives_retries` config options. +* Added new `SqlState` variants. +* Added more `Debug` impls. +* Added `GenericClient::batch_execute`. +* Added `RowStream::rows_affected`. +* Added the `tcp_user_timeout` config option. + +## Changed + +* Passing an incorrect number of parameters to a query method now returns an error instead of panicking. +* Upgraded `socket2`. + ## v0.7.7 - 2022-08-21 ## Added diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index c29852dec..e5451e2a2 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-postgres" -version = "0.7.7" +version = "0.7.8" authors = ["Steven Fackler "] edition = "2018" license = "MIT/Apache-2.0" From cf9747e3e54f5d0e12deb23ded2d896c4d16de39 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 19:20:24 -0400 Subject: [PATCH 057/207] Add tcp_user_timeout to postgres --- postgres/src/config.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index b541ec846..95c5ea417 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -44,6 +44,9 @@ use tokio_postgres::{Error, Socket}; /// omitted or the empty string. /// * `connect_timeout` - The time limit in seconds applied to each socket-level connection attempt. Note that hostnames /// can resolve to multiple IP addresses, and this limit is applied to each address. Defaults to no timeout. +/// * `tcp_user_timeout` - The time limit that transmitted data may remain unacknowledged before a connection is forcibly closed. +/// This is ignored for Unix domain socket connections. It is only supported on systems where TCP_USER_TIMEOUT is available +/// and will default to the system default if omitted or set to 0; on other systems, it has no effect. /// * `keepalives` - Controls the use of TCP keepalive. A value of 0 disables keepalive and nonzero integers enable it. /// This option is ignored when connecting with Unix sockets. Defaults to on. /// * `keepalives_idle` - The number of seconds of inactivity after which a keepalive message is sent to the server. @@ -256,6 +259,22 @@ impl Config { self.config.get_connect_timeout() } + /// Sets the TCP user timeout. + /// + /// This is ignored for Unix domain socket connections. It is only supported on systems where + /// TCP_USER_TIMEOUT is available and will default to the system default if omitted or set to 0; + /// on other systems, it has no effect. + pub fn tcp_user_timeout(&mut self, tcp_user_timeout: Duration) -> &mut Config { + self.config.tcp_user_timeout(tcp_user_timeout); + self + } + + /// Gets the TCP user timeout, if one has been set with the + /// `user_timeout` method. + pub fn get_tcp_user_timeout(&self) -> Option<&Duration> { + self.config.get_tcp_user_timeout() + } + /// Controls the use of TCP keepalive. /// /// This is ignored for Unix domain socket connections. Defaults to `true`. From 7d5962ef3ff811bbfe54cb069a9d0401cf1d92a5 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 19:24:48 -0400 Subject: [PATCH 058/207] Add RowIter::rows_affected --- postgres/src/row_iter.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/postgres/src/row_iter.rs b/postgres/src/row_iter.rs index 772e9893c..221fdfc68 100644 --- a/postgres/src/row_iter.rs +++ b/postgres/src/row_iter.rs @@ -17,6 +17,13 @@ impl<'a> RowIter<'a> { it: Box::pin(stream), } } + + /// Returns the number of rows affected by the query. + /// + /// This function will return `None` until the iterator has been exhausted. + pub fn rows_affected(&self) -> Option { + self.it.rows_affected() + } } impl FallibleIterator for RowIter<'_> { From 65a68dfa463d4779e709234bbcacbf952f265223 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 27 Mar 2023 19:27:16 -0400 Subject: [PATCH 059/207] Release postgres v0.19.5 --- postgres/CHANGELOG.md | 12 ++++++++++++ postgres/Cargo.toml | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/postgres/CHANGELOG.md b/postgres/CHANGELOG.md index c467c8b73..b8263a04a 100644 --- a/postgres/CHANGELOG.md +++ b/postgres/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## v0.19.5 - 2023-03-27 + +## Added + +* Added `keepalives_interval` and `keepalives_retries` config options. +* Added the `tcp_user_timeout` config option. +* Added `RowIter::rows_affected`. + +## Changed + +* Passing an incorrect number of parameters to a query method now returns an error instead of panicking. + ## v0.19.4 - 2022-08-21 ## Added diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index e3a8663f2..e0b2a249d 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres" -version = "0.19.4" +version = "0.19.5" authors = ["Steven Fackler "] edition = "2018" license = "MIT/Apache-2.0" @@ -39,7 +39,7 @@ with-time-0_3 = ["tokio-postgres/with-time-0_3"] bytes = "1.0" fallible-iterator = "0.2" futures-util = { version = "0.3.14", features = ["sink"] } -tokio-postgres = { version = "0.7.7", path = "../tokio-postgres" } +tokio-postgres = { version = "0.7.8", path = "../tokio-postgres" } tokio = { version = "1.0", features = ["rt", "time"] } log = "0.4" From f4b181a20180f1853351be53a32865b6209d0ab4 Mon Sep 17 00:00:00 2001 From: jaydenelliott Date: Tue, 28 Mar 2023 22:25:50 +1100 Subject: [PATCH 060/207] Rename_all attribute documentation --- postgres-derive-test/src/enums.rs | 10 +++++----- postgres-derive/src/fromsql.rs | 4 ++-- postgres-derive/src/tosql.rs | 4 ++-- postgres-types/src/lib.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/postgres-derive-test/src/enums.rs b/postgres-derive-test/src/enums.rs index e44f37616..36d428437 100644 --- a/postgres-derive-test/src/enums.rs +++ b/postgres-derive-test/src/enums.rs @@ -58,15 +58,15 @@ fn rename_all_overrides() { #[derive(Debug, ToSql, FromSql, PartialEq)] #[postgres(name = "mood", rename_all = "snake_case")] enum Mood { - Sad, + VerySad, #[postgres(name = "okay")] Ok, - Happy, + VeryHappy, } let mut conn = Client::connect("user=postgres host=localhost port=5433", NoTls).unwrap(); conn.execute( - "CREATE TYPE pg_temp.mood AS ENUM ('sad', 'okay', 'happy')", + "CREATE TYPE pg_temp.mood AS ENUM ('very_sad', 'okay', 'very_happy')", &[], ) .unwrap(); @@ -75,9 +75,9 @@ fn rename_all_overrides() { &mut conn, "mood", &[ - (Mood::Sad, "'sad'"), + (Mood::VerySad, "'very_sad'"), (Mood::Ok, "'okay'"), - (Mood::Happy, "'happy'"), + (Mood::VeryHappy, "'very_happy'"), ], ); } diff --git a/postgres-derive/src/fromsql.rs b/postgres-derive/src/fromsql.rs index 4deb23ed2..a9150411a 100644 --- a/postgres-derive/src/fromsql.rs +++ b/postgres-derive/src/fromsql.rs @@ -17,10 +17,10 @@ use crate::overrides::Overrides; pub fn expand_derive_fromsql(input: DeriveInput) -> Result { let overrides = Overrides::extract(&input.attrs, true)?; - if overrides.name.is_some() && overrides.transparent { + if (overrides.name.is_some() || overrides.rename_all.is_some()) && overrides.transparent { return Err(Error::new_spanned( &input, - "#[postgres(transparent)] is not allowed with #[postgres(name = \"...\")]", + "#[postgres(transparent)] is not allowed with #[postgres(name = \"...\")] or #[postgres(rename_all = \"...\")]", )); } diff --git a/postgres-derive/src/tosql.rs b/postgres-derive/src/tosql.rs index dbeeb16c3..ec7602312 100644 --- a/postgres-derive/src/tosql.rs +++ b/postgres-derive/src/tosql.rs @@ -15,10 +15,10 @@ use crate::overrides::Overrides; pub fn expand_derive_tosql(input: DeriveInput) -> Result { let overrides = Overrides::extract(&input.attrs, true)?; - if overrides.name.is_some() && overrides.transparent { + if (overrides.name.is_some() || overrides.rename_all.is_some()) && overrides.transparent { return Err(Error::new_spanned( &input, - "#[postgres(transparent)] is not allowed with #[postgres(name = \"...\")]", + "#[postgres(transparent)] is not allowed with #[postgres(name = \"...\")] or #[postgres(rename_all = \"...\")]", )); } diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index fa49d99eb..5fca049a7 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -125,6 +125,37 @@ //! Happy, //! } //! ``` +//! +//! Alternatively, the `#[postgres(rename_all = "...")]` attribute can be used to rename all fields or variants +//! with the chosen casing convention. This will not affect the struct or enum's type name. Note that +//! `#[postgres(name = "...")]` takes precendence when used in conjunction with `#[postgres(rename_all = "...")]`: +//! +//! ```rust +//! # #[cfg(feature = "derive")] +//! use postgres_types::{ToSql, FromSql}; +//! +//! # #[cfg(feature = "derive")] +//! #[derive(Debug, ToSql, FromSql)] +//! #[postgres(name = "mood", rename_all = "snake_case")] +//! enum Mood { +//! VerySad, // very_sad +//! #[postgres(name = "ok")] +//! Ok, // ok +//! VeryHappy, // very_happy +//! } +//! ``` +//! +//! The following case conventions are supported: +//! - `"lowercase"` +//! - `"UPPERCASE"` +//! - `"PascalCase"` +//! - `"camelCase"` +//! - `"snake_case"` +//! - `"SCREAMING_SNAKE_CASE"` +//! - `"kebab-case"` +//! - `"SCREAMING-KEBAB-CASE"` +//! - `"Train-Case"` + #![doc(html_root_url = "https://docs.rs/postgres-types/0.2")] #![warn(clippy::all, rust_2018_idioms, missing_docs)] From b19fdd4b7ecab1e30e56f55dc95de8d53f9d14da Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 30 Mar 2023 19:30:40 -0400 Subject: [PATCH 061/207] Fix postgres-protocol constraint Closes #1012 --- tokio-postgres/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index e5451e2a2..4dc93e3a2 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -53,7 +53,7 @@ parking_lot = "0.12" percent-encoding = "2.0" pin-project-lite = "0.2" phf = "0.11" -postgres-protocol = { version = "0.6.4", path = "../postgres-protocol" } +postgres-protocol = { version = "0.6.5", path = "../postgres-protocol" } postgres-types = { version = "0.2.4", path = "../postgres-types" } socket2 = { version = "0.5", features = ["all"] } tokio = { version = "1.27", features = ["io-util"] } From 45d51d708c645f0ebbd3d0dcf5f3eaad3d461916 Mon Sep 17 00:00:00 2001 From: Niklas Hallqvist Date: Tue, 4 Apr 2023 14:27:45 +0200 Subject: [PATCH 062/207] OpenBSD misses some TCP keepalive options --- tokio-postgres/src/keepalive.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tokio-postgres/src/keepalive.rs b/tokio-postgres/src/keepalive.rs index 74f453985..24d8d2c0e 100644 --- a/tokio-postgres/src/keepalive.rs +++ b/tokio-postgres/src/keepalive.rs @@ -12,12 +12,12 @@ impl From<&KeepaliveConfig> for TcpKeepalive { fn from(keepalive_config: &KeepaliveConfig) -> Self { let mut tcp_keepalive = Self::new().with_time(keepalive_config.idle); - #[cfg(not(any(target_os = "redox", target_os = "solaris")))] + #[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "openbsd")))] if let Some(interval) = keepalive_config.interval { tcp_keepalive = tcp_keepalive.with_interval(interval); } - #[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))] + #[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows", target_os = "openbsd")))] if let Some(retries) = keepalive_config.retries { tcp_keepalive = tcp_keepalive.with_retries(retries); } From e59a16524190db45eead594c61b6a9012ad3a3b9 Mon Sep 17 00:00:00 2001 From: Niklas Hallqvist Date: Tue, 4 Apr 2023 15:43:39 +0200 Subject: [PATCH 063/207] rustfmt --- tokio-postgres/src/keepalive.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tokio-postgres/src/keepalive.rs b/tokio-postgres/src/keepalive.rs index 24d8d2c0e..c409eb0ea 100644 --- a/tokio-postgres/src/keepalive.rs +++ b/tokio-postgres/src/keepalive.rs @@ -17,7 +17,12 @@ impl From<&KeepaliveConfig> for TcpKeepalive { tcp_keepalive = tcp_keepalive.with_interval(interval); } - #[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows", target_os = "openbsd")))] + #[cfg(not(any( + target_os = "redox", + target_os = "solaris", + target_os = "windows", + target_os = "openbsd" + )))] if let Some(retries) = keepalive_config.retries { tcp_keepalive = tcp_keepalive.with_retries(retries); } From a67fe643a9dc483530ba1df5cf09e3dfdec90c98 Mon Sep 17 00:00:00 2001 From: Basti Ortiz <39114273+BastiDood@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:39:37 +0800 Subject: [PATCH 064/207] refactor(types): simplify `<&str as ToSql>::to_sql` --- postgres-types/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index fa49d99eb..c34fbe66d 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -1012,10 +1012,10 @@ impl ToSql for Vec { impl<'a> ToSql for &'a str { fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { - match *ty { - ref ty if ty.name() == "ltree" => types::ltree_to_sql(self, w), - ref ty if ty.name() == "lquery" => types::lquery_to_sql(self, w), - ref ty if ty.name() == "ltxtquery" => types::ltxtquery_to_sql(self, w), + match ty.name() { + "ltree" => types::ltree_to_sql(self, w), + "lquery" => types::lquery_to_sql(self, w), + "ltxtquery" => types::ltxtquery_to_sql(self, w), _ => types::text_to_sql(self, w), } Ok(IsNull::No) From 98abdf9fa25a2e908fd62c5961655e00989fafa2 Mon Sep 17 00:00:00 2001 From: Basti Ortiz <39114273+BastiDood@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:43:25 +0800 Subject: [PATCH 065/207] refactor(types): prefer `matches!` macro for readability --- postgres-types/src/lib.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index c34fbe66d..291e069da 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -1022,18 +1022,10 @@ impl<'a> ToSql for &'a str { } fn accepts(ty: &Type) -> bool { - match *ty { - Type::VARCHAR | Type::TEXT | Type::BPCHAR | Type::NAME | Type::UNKNOWN => true, - ref ty - if (ty.name() == "citext" - || ty.name() == "ltree" - || ty.name() == "lquery" - || ty.name() == "ltxtquery") => - { - true - } - _ => false, - } + matches!( + *ty, + Type::VARCHAR | Type::TEXT | Type::BPCHAR | Type::NAME | Type::UNKNOWN + ) || matches!(ty.name(), "citext" | "ltree" | "lquery" | "ltxtquery") } to_sql_checked!(); From e71335ee43978311b2c1f253afef6c92abdaac88 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 1 May 2023 19:33:49 -0400 Subject: [PATCH 066/207] fix serialization of oidvector --- postgres-types/src/lib.rs | 8 +++++++- tokio-postgres/src/connect_socket.rs | 4 +++- tokio-postgres/tests/test/types/mod.rs | 11 +++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index 291e069da..c4c448c4a 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -910,9 +910,15 @@ impl<'a, T: ToSql> ToSql for &'a [T] { _ => panic!("expected array type"), }; + // Arrays are normally one indexed by default but oidvector *requires* zero indexing + let lower_bound = match *ty { + Type::OID_VECTOR => 0, + _ => 1, + }; + let dimension = ArrayDimension { len: downcast(self.len())?, - lower_bound: 1, + lower_bound, }; types::array_to_sql( diff --git a/tokio-postgres/src/connect_socket.rs b/tokio-postgres/src/connect_socket.rs index 9b3d31d72..1204ca1ff 100644 --- a/tokio-postgres/src/connect_socket.rs +++ b/tokio-postgres/src/connect_socket.rs @@ -14,7 +14,9 @@ pub(crate) async fn connect_socket( host: &Host, port: u16, connect_timeout: Option, - tcp_user_timeout: Option, + #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] tcp_user_timeout: Option< + Duration, + >, keepalive_config: Option<&KeepaliveConfig>, ) -> Result { match host { diff --git a/tokio-postgres/tests/test/types/mod.rs b/tokio-postgres/tests/test/types/mod.rs index 452d149fe..0f1d38242 100644 --- a/tokio-postgres/tests/test/types/mod.rs +++ b/tokio-postgres/tests/test/types/mod.rs @@ -739,3 +739,14 @@ async fn ltxtquery_any() { ) .await; } + +#[tokio::test] +async fn oidvector() { + test_type( + "oidvector", + // NB: postgres does not support empty oidarrays! All empty arrays are normalized to zero dimensions, but the + // oidvectorrecv function requires exactly one dimension. + &[(Some(vec![0u32, 1, 2]), "ARRAY[0,1,2]"), (None, "NULL")], + ) + .await; +} From d92b3b0a63e7abba41d56cebd06356d1a50db879 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 1 May 2023 19:45:54 -0400 Subject: [PATCH 067/207] Fix int2vector serialization --- postgres-types/src/lib.rs | 4 ++-- tokio-postgres/tests/test/types/mod.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index c4c448c4a..b03c389a9 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -910,9 +910,9 @@ impl<'a, T: ToSql> ToSql for &'a [T] { _ => panic!("expected array type"), }; - // Arrays are normally one indexed by default but oidvector *requires* zero indexing + // Arrays are normally one indexed by default but oidvector and int2vector *require* zero indexing let lower_bound = match *ty { - Type::OID_VECTOR => 0, + Type::OID_VECTOR | Type::INT2_VECTOR => 0, _ => 1, }; diff --git a/tokio-postgres/tests/test/types/mod.rs b/tokio-postgres/tests/test/types/mod.rs index 0f1d38242..f1a44da08 100644 --- a/tokio-postgres/tests/test/types/mod.rs +++ b/tokio-postgres/tests/test/types/mod.rs @@ -750,3 +750,14 @@ async fn oidvector() { ) .await; } + +#[tokio::test] +async fn int2vector() { + test_type( + "int2vector", + // NB: postgres does not support empty int2vectors! All empty arrays are normalized to zero dimensions, but the + // oidvectorrecv function requires exactly one dimension. + &[(Some(vec![0i16, 1, 2]), "ARRAY[0,1,2]"), (None, "NULL")], + ) + .await; +} From 80adf0448b95548dabd8354ae6988f801e7a5965 Mon Sep 17 00:00:00 2001 From: Ibiyemi Abiodun Date: Sun, 7 May 2023 13:37:52 -0400 Subject: [PATCH 068/207] allow `BorrowToSql` for non-static `Box` --- postgres-types/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index 291e069da..6517b4a95 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -1178,17 +1178,17 @@ impl BorrowToSql for &dyn ToSql { } } -impl sealed::Sealed for Box {} +impl<'a> sealed::Sealed for Box {} -impl BorrowToSql for Box { +impl<'a> BorrowToSql for Box { #[inline] fn borrow_to_sql(&self) -> &dyn ToSql { self.as_ref() } } -impl sealed::Sealed for Box {} -impl BorrowToSql for Box { +impl<'a> sealed::Sealed for Box {} +impl<'a> BorrowToSql for Box { #[inline] fn borrow_to_sql(&self) -> &dyn ToSql { self.as_ref() From 066b466f4443d0d51c6b1d409f3a2c93019ca27e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 7 May 2023 13:48:50 -0400 Subject: [PATCH 069/207] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8044b2f47..8e91c6faf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,7 @@ jobs: - run: docker compose up -d - uses: sfackler/actions/rustup@master with: - version: 1.64.0 + version: 1.65.0 - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - uses: actions/cache@v3 From 40954901a422838800a0f99608bf0ab308e5e9aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 14:01:30 +0000 Subject: [PATCH 070/207] Update criterion requirement from 0.4 to 0.5 Updates the requirements on [criterion](https://github.com/bheisler/criterion.rs) to permit the latest version. - [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/bheisler/criterion.rs/compare/0.4.0...0.5.0) --- updated-dependencies: - dependency-name: criterion dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- postgres/Cargo.toml | 2 +- tokio-postgres/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index e0b2a249d..044bb91e1 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -45,5 +45,5 @@ tokio = { version = "1.0", features = ["rt", "time"] } log = "0.4" [dev-dependencies] -criterion = "0.4" +criterion = "0.5" tokio = { version = "1.0", features = ["rt-multi-thread"] } diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 4dc93e3a2..b5c6d0ae6 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -61,7 +61,7 @@ tokio-util = { version = "0.7", features = ["codec"] } [dev-dependencies] futures-executor = "0.3" -criterion = "0.4" +criterion = "0.5" env_logger = "0.10" tokio = { version = "1.0", features = [ "macros", From 64bf779f7c91524b820e60226a6b8c8075d2dfa4 Mon Sep 17 00:00:00 2001 From: Zeb Piasecki Date: Sat, 3 Jun 2023 09:18:58 -0400 Subject: [PATCH 071/207] feat: add support for wasm Adds support for compiling to WASM environments that provide JS via wasm-bindgen. Because there's no standardized socket API the caller must provide a connection that implements AsyncRead/AsyncWrite to connect_raw. --- Cargo.toml | 1 + postgres-protocol/Cargo.toml | 3 +++ tokio-postgres/Cargo.toml | 4 +++- tokio-postgres/src/config.rs | 42 ++++++++++++++++++++++++++---------- tokio-postgres/src/lib.rs | 1 + 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4752836a7..80a7739c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "codegen", "postgres", diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index e32211369..1c6422e7d 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -19,3 +19,6 @@ memchr = "2.0" rand = "0.8" sha2 = "0.10" stringprep = "0.1" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2.9", features = ["js"] } diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index b5c6d0ae6..af0e6dee0 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -55,10 +55,12 @@ pin-project-lite = "0.2" phf = "0.11" postgres-protocol = { version = "0.6.5", path = "../postgres-protocol" } postgres-types = { version = "0.2.4", path = "../postgres-types" } -socket2 = { version = "0.5", features = ["all"] } tokio = { version = "1.27", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +socket2 = { version = "0.5", features = ["all"] } + [dev-dependencies] futures-executor = "0.3" criterion = "0.5" diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index a8aa7a9f5..2b2be08ef 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -3,6 +3,7 @@ #[cfg(feature = "runtime")] use crate::connect::connect; use crate::connect_raw::connect_raw; +#[cfg(not(target_arch = "wasm32"))] use crate::keepalive::KeepaliveConfig; #[cfg(feature = "runtime")] use crate::tls::MakeTlsConnect; @@ -165,6 +166,7 @@ pub struct Config { pub(crate) connect_timeout: Option, pub(crate) tcp_user_timeout: Option, pub(crate) keepalives: bool, + #[cfg(not(target_arch = "wasm32"))] pub(crate) keepalive_config: KeepaliveConfig, pub(crate) target_session_attrs: TargetSessionAttrs, pub(crate) channel_binding: ChannelBinding, @@ -179,11 +181,6 @@ impl Default for Config { impl Config { /// Creates a new configuration. pub fn new() -> Config { - let keepalive_config = KeepaliveConfig { - idle: Duration::from_secs(2 * 60 * 60), - interval: None, - retries: None, - }; Config { user: None, password: None, @@ -196,7 +193,12 @@ impl Config { connect_timeout: None, tcp_user_timeout: None, keepalives: true, - keepalive_config, + #[cfg(not(target_arch = "wasm32"))] + keepalive_config: KeepaliveConfig { + idle: Duration::from_secs(2 * 60 * 60), + interval: None, + retries: None, + }, target_session_attrs: TargetSessionAttrs::Any, channel_binding: ChannelBinding::Prefer, } @@ -377,6 +379,7 @@ impl Config { /// Sets the amount of idle time before a keepalive packet is sent on the connection. /// /// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled. Defaults to 2 hours. + #[cfg(not(target_arch = "wasm32"))] pub fn keepalives_idle(&mut self, keepalives_idle: Duration) -> &mut Config { self.keepalive_config.idle = keepalives_idle; self @@ -384,6 +387,7 @@ impl Config { /// Gets the configured amount of idle time before a keepalive packet will /// be sent on the connection. + #[cfg(not(target_arch = "wasm32"))] pub fn get_keepalives_idle(&self) -> Duration { self.keepalive_config.idle } @@ -392,12 +396,14 @@ impl Config { /// On Windows, this sets the value of the tcp_keepalive struct’s keepaliveinterval field. /// /// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled. + #[cfg(not(target_arch = "wasm32"))] pub fn keepalives_interval(&mut self, keepalives_interval: Duration) -> &mut Config { self.keepalive_config.interval = Some(keepalives_interval); self } /// Gets the time interval between TCP keepalive probes. + #[cfg(not(target_arch = "wasm32"))] pub fn get_keepalives_interval(&self) -> Option { self.keepalive_config.interval } @@ -405,12 +411,14 @@ impl Config { /// Sets the maximum number of TCP keepalive probes that will be sent before dropping a connection. /// /// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled. + #[cfg(not(target_arch = "wasm32"))] pub fn keepalives_retries(&mut self, keepalives_retries: u32) -> &mut Config { self.keepalive_config.retries = Some(keepalives_retries); self } /// Gets the maximum number of TCP keepalive probes that will be sent before dropping a connection. + #[cfg(not(target_arch = "wasm32"))] pub fn get_keepalives_retries(&self) -> Option { self.keepalive_config.retries } @@ -503,12 +511,14 @@ impl Config { self.tcp_user_timeout(Duration::from_secs(timeout as u64)); } } + #[cfg(not(target_arch = "wasm32"))] "keepalives" => { let keepalives = value .parse::() .map_err(|_| Error::config_parse(Box::new(InvalidValue("keepalives"))))?; self.keepalives(keepalives != 0); } + #[cfg(not(target_arch = "wasm32"))] "keepalives_idle" => { let keepalives_idle = value .parse::() @@ -517,6 +527,7 @@ impl Config { self.keepalives_idle(Duration::from_secs(keepalives_idle as u64)); } } + #[cfg(not(target_arch = "wasm32"))] "keepalives_interval" => { let keepalives_interval = value.parse::().map_err(|_| { Error::config_parse(Box::new(InvalidValue("keepalives_interval"))) @@ -525,6 +536,7 @@ impl Config { self.keepalives_interval(Duration::from_secs(keepalives_interval as u64)); } } + #[cfg(not(target_arch = "wasm32"))] "keepalives_retries" => { let keepalives_retries = value.parse::().map_err(|_| { Error::config_parse(Box::new(InvalidValue("keepalives_retries"))) @@ -614,7 +626,8 @@ impl fmt::Debug for Config { } } - f.debug_struct("Config") + let mut config_dbg = &mut f.debug_struct("Config"); + config_dbg = config_dbg .field("user", &self.user) .field("password", &self.password.as_ref().map(|_| Redaction {})) .field("dbname", &self.dbname) @@ -625,10 +638,17 @@ impl fmt::Debug for Config { .field("port", &self.port) .field("connect_timeout", &self.connect_timeout) .field("tcp_user_timeout", &self.tcp_user_timeout) - .field("keepalives", &self.keepalives) - .field("keepalives_idle", &self.keepalive_config.idle) - .field("keepalives_interval", &self.keepalive_config.interval) - .field("keepalives_retries", &self.keepalive_config.retries) + .field("keepalives", &self.keepalives); + + #[cfg(not(target_arch = "wasm32"))] + { + config_dbg = config_dbg + .field("keepalives_idle", &self.keepalive_config.idle) + .field("keepalives_interval", &self.keepalive_config.interval) + .field("keepalives_retries", &self.keepalive_config.retries); + } + + config_dbg .field("target_session_attrs", &self.target_session_attrs) .field("channel_binding", &self.channel_binding) .finish() diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs index a9ecba4f1..2bb410187 100644 --- a/tokio-postgres/src/lib.rs +++ b/tokio-postgres/src/lib.rs @@ -163,6 +163,7 @@ mod copy_in; mod copy_out; pub mod error; mod generic_client; +#[cfg(not(target_arch = "wasm32"))] mod keepalive; mod maybe_tls_stream; mod portal; From 2230e88533acccf5632b2d43aff315c88a2507a2 Mon Sep 17 00:00:00 2001 From: Zeb Piasecki Date: Sat, 3 Jun 2023 17:32:48 -0400 Subject: [PATCH 072/207] add CI job for checking wasm Adds a CI job for ensuring the tokio-postgres crate builds on the wasm32-unknown-unknown target without the default features. --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e91c6faf..46f97e48f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,33 @@ jobs: key: clippy-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}y - run: cargo clippy --all --all-targets + check-wasm32: + name: check-wasm32 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: sfackler/actions/rustup@master + - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT + id: rust-version + - run: rustup target add wasm32-unknown-unknown + - uses: actions/cache@v3 + with: + path: ~/.cargo/registry/index + key: index-${{ runner.os }}-${{ github.run_number }} + restore-keys: | + index-${{ runner.os }}- + - run: cargo generate-lockfile + - uses: actions/cache@v3 + with: + path: ~/.cargo/registry/cache + key: registry-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }} + - run: cargo fetch + - uses: actions/cache@v3 + with: + path: target + key: clippy-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}y + - run: cargo check --target wasm32-unknown-unknown --manifest-path tokio-postgres/Cargo.toml --no-default-features + test: name: test runs-on: ubuntu-latest From edc7fdecfb9f81b923bfe904edefd41e7076fa8c Mon Sep 17 00:00:00 2001 From: Zeb Piasecki Date: Sun, 4 Jun 2023 13:02:03 -0400 Subject: [PATCH 073/207] gate wasm support behind feature flag --- Cargo.toml | 1 - postgres-protocol/Cargo.toml | 8 +++++--- tokio-postgres/Cargo.toml | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 80a7739c8..4752836a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ [workspace] -resolver = "2" members = [ "codegen", "postgres", diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index 1c6422e7d..ad609f6fa 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -8,6 +8,10 @@ license = "MIT/Apache-2.0" repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" +[features] +default = [] +js = ["getrandom/js"] + [dependencies] base64 = "0.21" byteorder = "1.0" @@ -19,6 +23,4 @@ memchr = "2.0" rand = "0.8" sha2 = "0.10" stringprep = "0.1" - -[target.'cfg(target_arch = "wasm32")'.dependencies] -getrandom = { version = "0.2.9", features = ["js"] } +getrandom = { version = "0.2", optional = true } diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index af0e6dee0..12d8a66fd 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -40,6 +40,7 @@ with-uuid-0_8 = ["postgres-types/with-uuid-0_8"] with-uuid-1 = ["postgres-types/with-uuid-1"] with-time-0_2 = ["postgres-types/with-time-0_2"] with-time-0_3 = ["postgres-types/with-time-0_3"] +js = ["postgres-protocol/js"] [dependencies] async-trait = "0.1" From 1f8fb7a16c131ed50a46fc139838327e8a604775 Mon Sep 17 00:00:00 2001 From: Zeb Piasecki Date: Wed, 7 Jun 2023 21:17:54 -0400 Subject: [PATCH 074/207] ignore dev deps in wasm ci --- .github/workflows/ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46f97e48f..99cf652d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v3 - uses: sfackler/actions/rustup@master - uses: sfackler/actions/rustfmt@master - + clippy: name: clippy runs-on: ubuntu-latest @@ -72,7 +72,12 @@ jobs: with: path: target key: clippy-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}y - - run: cargo check --target wasm32-unknown-unknown --manifest-path tokio-postgres/Cargo.toml --no-default-features + - run: | + # Hack: wasm support currently relies on not having tokio with features like socket enabled. With resolver 1 + # dev dependencies can add unwanted dependencies to the build, so we'll hackily disable them for this check. + + sed -i 's/\[dev-dependencies]/[ignore-dependencies]/g' ./tokio-postgres/Cargo.toml + cargo check --target wasm32-unknown-unknown --manifest-path tokio-postgres/Cargo.toml --no-default-features test: name: test From 635bac4665d4a744a523e6d843f67ffed33b6cff Mon Sep 17 00:00:00 2001 From: Zeb Piasecki Date: Fri, 9 Jun 2023 11:15:06 -0400 Subject: [PATCH 075/207] specify js feature for wasm ci --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99cf652d2..0064369c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,7 @@ jobs: # dev dependencies can add unwanted dependencies to the build, so we'll hackily disable them for this check. sed -i 's/\[dev-dependencies]/[ignore-dependencies]/g' ./tokio-postgres/Cargo.toml - cargo check --target wasm32-unknown-unknown --manifest-path tokio-postgres/Cargo.toml --no-default-features + cargo check --target wasm32-unknown-unknown --manifest-path tokio-postgres/Cargo.toml --no-default-features --features js test: name: test From 6f19bb9000bd5e53cd7613f0f96a24c3657533b6 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 10 Jun 2023 10:21:34 -0400 Subject: [PATCH 076/207] clean up wasm32 test --- .github/workflows/ci.yml | 9 ++------- Cargo.toml | 1 + 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0064369c9..ebe0f600f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,13 +71,8 @@ jobs: - uses: actions/cache@v3 with: path: target - key: clippy-target-${{ runner.os }}-${{ steps.rust-version.outputs.version }}-${{ hashFiles('Cargo.lock') }}y - - run: | - # Hack: wasm support currently relies on not having tokio with features like socket enabled. With resolver 1 - # dev dependencies can add unwanted dependencies to the build, so we'll hackily disable them for this check. - - sed -i 's/\[dev-dependencies]/[ignore-dependencies]/g' ./tokio-postgres/Cargo.toml - cargo check --target wasm32-unknown-unknown --manifest-path tokio-postgres/Cargo.toml --no-default-features --features js + 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 test: name: test diff --git a/Cargo.toml b/Cargo.toml index 4752836a7..16e3739dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "postgres-types", "tokio-postgres", ] +resolver = "2" [profile.release] debug = 2 From 258fe68f193b7951e20f244ecbbf664d7629f0eb Mon Sep 17 00:00:00 2001 From: Vinicius Hirschle Date: Sat, 29 Apr 2023 21:52:01 -0300 Subject: [PATCH 077/207] feat(derive): add `#[postgres(allow_mismatch)]` --- .../compile-fail/invalid-allow-mismatch.rs | 31 ++++++++ .../invalid-allow-mismatch.stderr | 43 +++++++++++ postgres-derive-test/src/enums.rs | 72 ++++++++++++++++++- postgres-derive/src/accepts.rs | 42 ++++++----- postgres-derive/src/fromsql.rs | 22 +++++- postgres-derive/src/overrides.rs | 22 +++++- postgres-derive/src/tosql.rs | 22 +++++- postgres-types/src/lib.rs | 23 +++++- 8 files changed, 250 insertions(+), 27 deletions(-) create mode 100644 postgres-derive-test/src/compile-fail/invalid-allow-mismatch.rs create mode 100644 postgres-derive-test/src/compile-fail/invalid-allow-mismatch.stderr diff --git a/postgres-derive-test/src/compile-fail/invalid-allow-mismatch.rs b/postgres-derive-test/src/compile-fail/invalid-allow-mismatch.rs new file mode 100644 index 000000000..52d0ba8f6 --- /dev/null +++ b/postgres-derive-test/src/compile-fail/invalid-allow-mismatch.rs @@ -0,0 +1,31 @@ +use postgres_types::{FromSql, ToSql}; + +#[derive(ToSql, Debug)] +#[postgres(allow_mismatch)] +struct ToSqlAllowMismatchStruct { + a: i32, +} + +#[derive(FromSql, Debug)] +#[postgres(allow_mismatch)] +struct FromSqlAllowMismatchStruct { + a: i32, +} + +#[derive(ToSql, Debug)] +#[postgres(allow_mismatch)] +struct ToSqlAllowMismatchTupleStruct(i32, i32); + +#[derive(FromSql, Debug)] +#[postgres(allow_mismatch)] +struct FromSqlAllowMismatchTupleStruct(i32, i32); + +#[derive(FromSql, Debug)] +#[postgres(transparent, allow_mismatch)] +struct TransparentFromSqlAllowMismatchStruct(i32); + +#[derive(FromSql, Debug)] +#[postgres(allow_mismatch, transparent)] +struct AllowMismatchFromSqlTransparentStruct(i32); + +fn main() {} diff --git a/postgres-derive-test/src/compile-fail/invalid-allow-mismatch.stderr b/postgres-derive-test/src/compile-fail/invalid-allow-mismatch.stderr new file mode 100644 index 000000000..a8e573248 --- /dev/null +++ b/postgres-derive-test/src/compile-fail/invalid-allow-mismatch.stderr @@ -0,0 +1,43 @@ +error: #[postgres(allow_mismatch)] may only be applied to enums + --> src/compile-fail/invalid-allow-mismatch.rs:4:1 + | +4 | / #[postgres(allow_mismatch)] +5 | | struct ToSqlAllowMismatchStruct { +6 | | a: i32, +7 | | } + | |_^ + +error: #[postgres(allow_mismatch)] may only be applied to enums + --> src/compile-fail/invalid-allow-mismatch.rs:10:1 + | +10 | / #[postgres(allow_mismatch)] +11 | | struct FromSqlAllowMismatchStruct { +12 | | a: i32, +13 | | } + | |_^ + +error: #[postgres(allow_mismatch)] may only be applied to enums + --> src/compile-fail/invalid-allow-mismatch.rs:16:1 + | +16 | / #[postgres(allow_mismatch)] +17 | | struct ToSqlAllowMismatchTupleStruct(i32, i32); + | |_______________________________________________^ + +error: #[postgres(allow_mismatch)] may only be applied to enums + --> src/compile-fail/invalid-allow-mismatch.rs:20:1 + | +20 | / #[postgres(allow_mismatch)] +21 | | struct FromSqlAllowMismatchTupleStruct(i32, i32); + | |_________________________________________________^ + +error: #[postgres(transparent)] is not allowed with #[postgres(allow_mismatch)] + --> src/compile-fail/invalid-allow-mismatch.rs:24:25 + | +24 | #[postgres(transparent, allow_mismatch)] + | ^^^^^^^^^^^^^^ + +error: #[postgres(allow_mismatch)] is not allowed with #[postgres(transparent)] + --> src/compile-fail/invalid-allow-mismatch.rs:28:28 + | +28 | #[postgres(allow_mismatch, transparent)] + | ^^^^^^^^^^^ diff --git a/postgres-derive-test/src/enums.rs b/postgres-derive-test/src/enums.rs index 36d428437..f3e6c488c 100644 --- a/postgres-derive-test/src/enums.rs +++ b/postgres-derive-test/src/enums.rs @@ -1,5 +1,5 @@ use crate::test_type; -use postgres::{Client, NoTls}; +use postgres::{error::DbError, Client, NoTls}; use postgres_types::{FromSql, ToSql, WrongType}; use std::error::Error; @@ -131,3 +131,73 @@ fn missing_variant() { let err = conn.execute("SELECT $1::foo", &[&Foo::Bar]).unwrap_err(); assert!(err.source().unwrap().is::()); } + +#[test] +fn allow_mismatch_enums() { + #[derive(Debug, ToSql, FromSql, PartialEq)] + #[postgres(allow_mismatch)] + enum Foo { + Bar, + } + + let mut conn = Client::connect("user=postgres host=localhost port=5433", NoTls).unwrap(); + conn.execute("CREATE TYPE pg_temp.\"Foo\" AS ENUM ('Bar', 'Baz')", &[]) + .unwrap(); + + let row = conn.query_one("SELECT $1::\"Foo\"", &[&Foo::Bar]).unwrap(); + assert_eq!(row.get::<_, Foo>(0), Foo::Bar); +} + +#[test] +fn missing_enum_variant() { + #[derive(Debug, ToSql, FromSql, PartialEq)] + #[postgres(allow_mismatch)] + enum Foo { + Bar, + Buz, + } + + let mut conn = Client::connect("user=postgres host=localhost port=5433", NoTls).unwrap(); + conn.execute("CREATE TYPE pg_temp.\"Foo\" AS ENUM ('Bar', 'Baz')", &[]) + .unwrap(); + + let err = conn + .query_one("SELECT $1::\"Foo\"", &[&Foo::Buz]) + .unwrap_err(); + assert!(err.source().unwrap().is::()); +} + +#[test] +fn allow_mismatch_and_renaming() { + #[derive(Debug, ToSql, FromSql, PartialEq)] + #[postgres(name = "foo", allow_mismatch)] + enum Foo { + #[postgres(name = "bar")] + Bar, + #[postgres(name = "buz")] + Buz, + } + + let mut conn = Client::connect("user=postgres host=localhost port=5433", NoTls).unwrap(); + conn.execute("CREATE TYPE pg_temp.foo AS ENUM ('bar', 'baz', 'buz')", &[]) + .unwrap(); + + let row = conn.query_one("SELECT $1::foo", &[&Foo::Buz]).unwrap(); + assert_eq!(row.get::<_, Foo>(0), Foo::Buz); +} + +#[test] +fn wrong_name_and_allow_mismatch() { + #[derive(Debug, ToSql, FromSql, PartialEq)] + #[postgres(allow_mismatch)] + enum Foo { + Bar, + } + + let mut conn = Client::connect("user=postgres host=localhost port=5433", NoTls).unwrap(); + conn.execute("CREATE TYPE pg_temp.foo AS ENUM ('Bar', 'Baz')", &[]) + .unwrap(); + + let err = conn.query_one("SELECT $1::foo", &[&Foo::Bar]).unwrap_err(); + assert!(err.source().unwrap().is::()); +} diff --git a/postgres-derive/src/accepts.rs b/postgres-derive/src/accepts.rs index 63473863a..a68538dcc 100644 --- a/postgres-derive/src/accepts.rs +++ b/postgres-derive/src/accepts.rs @@ -31,31 +31,37 @@ pub fn domain_body(name: &str, field: &syn::Field) -> TokenStream { } } -pub fn enum_body(name: &str, variants: &[Variant]) -> TokenStream { +pub fn enum_body(name: &str, variants: &[Variant], allow_mismatch: bool) -> TokenStream { let num_variants = variants.len(); let variant_names = variants.iter().map(|v| &v.name); - quote! { - if type_.name() != #name { - return false; + if allow_mismatch { + quote! { + type_.name() == #name } + } else { + quote! { + if type_.name() != #name { + return false; + } - match *type_.kind() { - ::postgres_types::Kind::Enum(ref variants) => { - if variants.len() != #num_variants { - return false; - } - - variants.iter().all(|v| { - match &**v { - #( - #variant_names => true, - )* - _ => false, + match *type_.kind() { + ::postgres_types::Kind::Enum(ref variants) => { + if variants.len() != #num_variants { + return false; } - }) + + variants.iter().all(|v| { + match &**v { + #( + #variant_names => true, + )* + _ => false, + } + }) + } + _ => false, } - _ => false, } } } diff --git a/postgres-derive/src/fromsql.rs b/postgres-derive/src/fromsql.rs index a9150411a..d3ac47f4f 100644 --- a/postgres-derive/src/fromsql.rs +++ b/postgres-derive/src/fromsql.rs @@ -48,6 +48,26 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result { )) } } + } else if overrides.allow_mismatch { + match input.data { + Data::Enum(ref data) => { + let variants = data + .variants + .iter() + .map(|variant| Variant::parse(variant, overrides.rename_all)) + .collect::, _>>()?; + ( + accepts::enum_body(&name, &variants, overrides.allow_mismatch), + enum_body(&input.ident, &variants), + ) + } + _ => { + return Err(Error::new_spanned( + input, + "#[postgres(allow_mismatch)] may only be applied to enums", + )); + } + } } else { match input.data { Data::Enum(ref data) => { @@ -57,7 +77,7 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result { .map(|variant| Variant::parse(variant, overrides.rename_all)) .collect::, _>>()?; ( - accepts::enum_body(&name, &variants), + accepts::enum_body(&name, &variants, overrides.allow_mismatch), enum_body(&input.ident, &variants), ) } diff --git a/postgres-derive/src/overrides.rs b/postgres-derive/src/overrides.rs index 99faeebb7..d50550bee 100644 --- a/postgres-derive/src/overrides.rs +++ b/postgres-derive/src/overrides.rs @@ -7,6 +7,7 @@ pub struct Overrides { pub name: Option, pub rename_all: Option, pub transparent: bool, + pub allow_mismatch: bool, } impl Overrides { @@ -15,6 +16,7 @@ impl Overrides { name: None, rename_all: None, transparent: false, + allow_mismatch: false, }; for attr in attrs { @@ -74,11 +76,25 @@ impl Overrides { } } Meta::Path(path) => { - if !path.is_ident("transparent") { + if path.is_ident("transparent") { + if overrides.allow_mismatch { + return Err(Error::new_spanned( + path, + "#[postgres(allow_mismatch)] is not allowed with #[postgres(transparent)]", + )); + } + overrides.transparent = true; + } else if path.is_ident("allow_mismatch") { + if overrides.transparent { + return Err(Error::new_spanned( + path, + "#[postgres(transparent)] is not allowed with #[postgres(allow_mismatch)]", + )); + } + overrides.allow_mismatch = true; + } else { return Err(Error::new_spanned(path, "unknown override")); } - - overrides.transparent = true; } bad => return Err(Error::new_spanned(bad, "unknown attribute")), } diff --git a/postgres-derive/src/tosql.rs b/postgres-derive/src/tosql.rs index ec7602312..81d4834bf 100644 --- a/postgres-derive/src/tosql.rs +++ b/postgres-derive/src/tosql.rs @@ -44,6 +44,26 @@ pub fn expand_derive_tosql(input: DeriveInput) -> Result { )); } } + } else if overrides.allow_mismatch { + match input.data { + Data::Enum(ref data) => { + let variants = data + .variants + .iter() + .map(|variant| Variant::parse(variant, overrides.rename_all)) + .collect::, _>>()?; + ( + accepts::enum_body(&name, &variants, overrides.allow_mismatch), + enum_body(&input.ident, &variants), + ) + } + _ => { + return Err(Error::new_spanned( + input, + "#[postgres(allow_mismatch)] may only be applied to enums", + )); + } + } } else { match input.data { Data::Enum(ref data) => { @@ -53,7 +73,7 @@ pub fn expand_derive_tosql(input: DeriveInput) -> Result { .map(|variant| Variant::parse(variant, overrides.rename_all)) .collect::, _>>()?; ( - accepts::enum_body(&name, &variants), + accepts::enum_body(&name, &variants, overrides.allow_mismatch), enum_body(&input.ident, &variants), ) } diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index edd723977..cb82e2f93 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -138,7 +138,6 @@ //! #[derive(Debug, ToSql, FromSql)] //! #[postgres(name = "mood", rename_all = "snake_case")] //! enum Mood { -//! VerySad, // very_sad //! #[postgres(name = "ok")] //! Ok, // ok //! VeryHappy, // very_happy @@ -155,10 +154,28 @@ //! - `"kebab-case"` //! - `"SCREAMING-KEBAB-CASE"` //! - `"Train-Case"` - +//! +//! ## Allowing Enum Mismatches +//! +//! By default the generated implementation of [`ToSql`] & [`FromSql`] for enums will require an exact match of the enum +//! variants between the Rust and Postgres types. +//! To allow mismatches, the `#[postgres(allow_mismatch)]` attribute can be used on the enum definition: +//! +//! ```sql +//! CREATE TYPE mood AS ENUM ( +//! 'Sad', +//! 'Ok', +//! 'Happy' +//! ); +//! ``` +//! #[postgres(allow_mismatch)] +//! enum Mood { +//! Happy, +//! Meh, +//! } +//! ``` #![doc(html_root_url = "https://docs.rs/postgres-types/0.2")] #![warn(clippy::all, rust_2018_idioms, missing_docs)] - use fallible_iterator::FallibleIterator; use postgres_protocol::types::{self, ArrayDimension}; use std::any::type_name; From b09e9cc6426728a9df665992a6a1e8cb2c4afbec Mon Sep 17 00:00:00 2001 From: Andrew Baxter Date: Thu, 20 Jul 2023 22:54:19 +0900 Subject: [PATCH 078/207] Add to_sql for bytes Cow as well --- postgres-types/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index edd723977..34c8cc0b8 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -1035,6 +1035,18 @@ impl ToSql for Box<[T]> { to_sql_checked!(); } +impl<'a> ToSql for Cow<'a, [u8]> { + fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { + <&str as ToSql>::to_sql(&self.as_ref(), ty, w) + } + + fn accepts(ty: &Type) -> bool { + <&[u8] as ToSql>::accepts(ty) + } + + to_sql_checked!(); +} + impl ToSql for Vec { fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { <&[u8] as ToSql>::to_sql(&&**self, ty, w) From 34c8dc9d1957f6b663c4236217ec7134ad1d3c5b Mon Sep 17 00:00:00 2001 From: andrew <> Date: Thu, 20 Jul 2023 23:30:27 +0900 Subject: [PATCH 079/207] Fixes --- postgres-types/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index 34c8cc0b8..1f56c468f 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -1037,7 +1037,7 @@ impl ToSql for Box<[T]> { impl<'a> ToSql for Cow<'a, [u8]> { fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result> { - <&str as ToSql>::to_sql(&self.as_ref(), ty, w) + <&[u8] as ToSql>::to_sql(&self.as_ref(), ty, w) } fn accepts(ty: &Type) -> bool { From f7a264473d8ba78a280f1fe173ecb9f3662be7f3 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 22 Jul 2023 20:40:47 -0400 Subject: [PATCH 080/207] align hostaddr tls behavior with documentation --- tokio-postgres/src/cancel_query.rs | 14 +++++--------- tokio-postgres/src/cancel_query_raw.rs | 2 +- tokio-postgres/src/cancel_token.rs | 2 +- tokio-postgres/src/client.rs | 1 + tokio-postgres/src/config.rs | 6 +++--- tokio-postgres/src/connect.rs | 25 ++++++++++++++----------- tokio-postgres/src/connect_raw.rs | 2 +- tokio-postgres/src/connect_tls.rs | 9 +++++++-- 8 files changed, 33 insertions(+), 28 deletions(-) diff --git a/tokio-postgres/src/cancel_query.rs b/tokio-postgres/src/cancel_query.rs index d869b5824..8e35a4224 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::{Host, SslMode}; +use crate::config::SslMode; use crate::tls::MakeTlsConnect; use crate::{cancel_query_raw, connect_socket, Error, Socket}; use std::io; @@ -24,14 +24,10 @@ where } }; - let hostname = match &config.host { - Host::Tcp(host) => &**host, - // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter - #[cfg(unix)] - Host::Unix(_) => "", - }; - let tls = tls - .make_tls_connect(hostname) + let tls = config + .hostname + .map(|s| tls.make_tls_connect(&s)) + .transpose() .map_err(|e| Error::tls(e.into()))?; let socket = connect_socket::connect_socket( diff --git a/tokio-postgres/src/cancel_query_raw.rs b/tokio-postgres/src/cancel_query_raw.rs index c89dc581f..cae887183 100644 --- a/tokio-postgres/src/cancel_query_raw.rs +++ b/tokio-postgres/src/cancel_query_raw.rs @@ -8,7 +8,7 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; pub async fn cancel_query_raw( stream: S, mode: SslMode, - tls: T, + tls: Option, process_id: i32, secret_key: i32, ) -> Result<(), Error> diff --git a/tokio-postgres/src/cancel_token.rs b/tokio-postgres/src/cancel_token.rs index d048a3c82..9671de726 100644 --- a/tokio-postgres/src/cancel_token.rs +++ b/tokio-postgres/src/cancel_token.rs @@ -54,7 +54,7 @@ impl CancelToken { cancel_query_raw::cancel_query_raw( stream, self.ssl_mode, - tls, + Some(tls), self.process_id, self.secret_key, ) diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index 8b7df4e87..ac486813e 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -154,6 +154,7 @@ impl InnerClient { #[derive(Clone)] pub(crate) struct SocketConfig { pub host: Host, + pub hostname: Option, pub port: u16, pub connect_timeout: Option, pub tcp_user_timeout: Option, diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index b18e3b8af..c88c5ff35 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -97,9 +97,9 @@ pub enum Host { /// * `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, -/// - or if host specifies an IP address, that value will be used directly. +/// or if host specifies an IP address, that value will be used directly. /// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications -/// with time constraints. However, a host name is required for verify-full SSL certificate verification. +/// with time constraints. However, a host name is required for TLS certificate verification. /// Specifically: /// * If `hostaddr` is specified without `host`, the value for `hostaddr` gives the server network address. /// The connection attempt will fail if the authentication method requires a host name; @@ -645,7 +645,7 @@ impl Config { S: AsyncRead + AsyncWrite + Unpin, T: TlsConnect, { - connect_raw(stream, tls, self).await + connect_raw(stream, Some(tls), self).await } } diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 32a0a76b9..abb1a0118 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -52,16 +52,17 @@ where .unwrap_or(5432); // The value of host is used as the hostname for TLS validation, - // if it's not present, use the value of hostaddr. let hostname = match host { - Some(Host::Tcp(host)) => host.clone(), + Some(Host::Tcp(host)) => Some(host.clone()), // postgres doesn't support TLS over unix sockets, so the choice here doesn't matter #[cfg(unix)] - Some(Host::Unix(_)) => "".to_string(), - None => hostaddr.map_or("".to_string(), |ipaddr| ipaddr.to_string()), + Some(Host::Unix(_)) => None, + None => None, }; - let tls = tls - .make_tls_connect(&hostname) + let tls = hostname + .as_ref() + .map(|s| tls.make_tls_connect(s)) + .transpose() .map_err(|e| Error::tls(e.into()))?; // Try to use the value of hostaddr to establish the TCP connection, @@ -78,7 +79,7 @@ where } }; - match connect_once(&addr, port, tls, config).await { + match connect_once(addr, hostname, port, tls, config).await { Ok((client, connection)) => return Ok((client, connection)), Err(e) => error = Some(e), } @@ -88,16 +89,17 @@ where } async fn connect_once( - host: &Host, + host: Host, + hostname: Option, port: u16, - tls: T, + tls: Option, config: &Config, ) -> Result<(Client, Connection), Error> where T: TlsConnect, { let socket = connect_socket( - host, + &host, port, config.connect_timeout, config.tcp_user_timeout, @@ -151,7 +153,8 @@ where } client.set_socket_config(SocketConfig { - host: host.clone(), + host, + hostname, port, connect_timeout: config.connect_timeout, tcp_user_timeout: config.tcp_user_timeout, diff --git a/tokio-postgres/src/connect_raw.rs b/tokio-postgres/src/connect_raw.rs index d97636221..2db6a66b9 100644 --- a/tokio-postgres/src/connect_raw.rs +++ b/tokio-postgres/src/connect_raw.rs @@ -80,7 +80,7 @@ where pub async fn connect_raw( stream: S, - tls: T, + tls: Option, config: &Config, ) -> Result<(Client, Connection), Error> where diff --git a/tokio-postgres/src/connect_tls.rs b/tokio-postgres/src/connect_tls.rs index 5ef21ac5c..d75dcde90 100644 --- a/tokio-postgres/src/connect_tls.rs +++ b/tokio-postgres/src/connect_tls.rs @@ -10,7 +10,7 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; pub async fn connect_tls( mut stream: S, mode: SslMode, - tls: T, + tls: Option, ) -> Result, Error> where S: AsyncRead + AsyncWrite + Unpin, @@ -18,7 +18,11 @@ where { match mode { SslMode::Disable => return Ok(MaybeTlsStream::Raw(stream)), - SslMode::Prefer if !tls.can_connect(ForcePrivateApi) => { + SslMode::Prefer + if tls + .as_ref() + .map_or(false, |tls| !tls.can_connect(ForcePrivateApi)) => + { return Ok(MaybeTlsStream::Raw(stream)) } SslMode::Prefer | SslMode::Require => {} @@ -40,6 +44,7 @@ where } let stream = tls + .ok_or_else(|| Error::tls("no hostname provided for TLS handshake".into()))? .connect(stream) .await .map_err(|e| Error::tls(e.into()))?; From b57574598ec0985d9b471144fe038886b6d8b92a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 22 Jul 2023 21:09:08 -0400 Subject: [PATCH 081/207] fix test --- tokio-postgres/src/cancel_query.rs | 10 +++++----- tokio-postgres/src/cancel_query_raw.rs | 5 +++-- tokio-postgres/src/cancel_token.rs | 3 ++- tokio-postgres/src/config.rs | 2 +- tokio-postgres/src/connect.rs | 11 +++++------ tokio-postgres/src/connect_raw.rs | 5 +++-- tokio-postgres/src/connect_tls.rs | 14 +++++++------- 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/tokio-postgres/src/cancel_query.rs b/tokio-postgres/src/cancel_query.rs index 8e35a4224..4a7766d60 100644 --- a/tokio-postgres/src/cancel_query.rs +++ b/tokio-postgres/src/cancel_query.rs @@ -24,11 +24,10 @@ where } }; - let tls = config - .hostname - .map(|s| tls.make_tls_connect(&s)) - .transpose() + let tls = tls + .make_tls_connect(config.hostname.as_deref().unwrap_or("")) .map_err(|e| Error::tls(e.into()))?; + let has_hostname = config.hostname.is_some(); let socket = connect_socket::connect_socket( &config.host, @@ -39,5 +38,6 @@ where ) .await?; - cancel_query_raw::cancel_query_raw(socket, ssl_mode, tls, process_id, secret_key).await + cancel_query_raw::cancel_query_raw(socket, ssl_mode, 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 cae887183..41aafe7d9 100644 --- a/tokio-postgres/src/cancel_query_raw.rs +++ b/tokio-postgres/src/cancel_query_raw.rs @@ -8,7 +8,8 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; pub async fn cancel_query_raw( stream: S, mode: SslMode, - tls: Option, + tls: T, + has_hostname: bool, process_id: i32, secret_key: i32, ) -> Result<(), Error> @@ -16,7 +17,7 @@ where S: AsyncRead + AsyncWrite + Unpin, T: TlsConnect, { - let mut stream = connect_tls::connect_tls(stream, mode, tls).await?; + let mut stream = connect_tls::connect_tls(stream, mode, 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 9671de726..c925ce0ca 100644 --- a/tokio-postgres/src/cancel_token.rs +++ b/tokio-postgres/src/cancel_token.rs @@ -54,7 +54,8 @@ impl CancelToken { cancel_query_raw::cancel_query_raw( stream, self.ssl_mode, - Some(tls), + tls, + true, self.process_id, self.secret_key, ) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index c88c5ff35..a7fa19312 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -645,7 +645,7 @@ impl Config { S: AsyncRead + AsyncWrite + Unpin, T: TlsConnect, { - connect_raw(stream, Some(tls), self).await + connect_raw(stream, tls, true, self).await } } diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index abb1a0118..441ad1238 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -59,10 +59,8 @@ where Some(Host::Unix(_)) => None, None => None, }; - let tls = hostname - .as_ref() - .map(|s| tls.make_tls_connect(s)) - .transpose() + let tls = tls + .make_tls_connect(hostname.as_deref().unwrap_or("")) .map_err(|e| Error::tls(e.into()))?; // Try to use the value of hostaddr to establish the TCP connection, @@ -92,7 +90,7 @@ async fn connect_once( host: Host, hostname: Option, port: u16, - tls: Option, + tls: T, config: &Config, ) -> Result<(Client, Connection), Error> where @@ -110,7 +108,8 @@ where }, ) .await?; - let (mut client, mut connection) = connect_raw(socket, tls, config).await?; + let has_hostname = hostname.is_some(); + let (mut client, mut connection) = connect_raw(socket, tls, has_hostname, config).await?; if let TargetSessionAttrs::ReadWrite = config.target_session_attrs { let rows = client.simple_query_raw("SHOW transaction_read_only"); diff --git a/tokio-postgres/src/connect_raw.rs b/tokio-postgres/src/connect_raw.rs index 2db6a66b9..254ca9f0c 100644 --- a/tokio-postgres/src/connect_raw.rs +++ b/tokio-postgres/src/connect_raw.rs @@ -80,14 +80,15 @@ where pub async fn connect_raw( stream: S, - tls: Option, + tls: T, + has_hostname: bool, config: &Config, ) -> Result<(Client, Connection), Error> where S: AsyncRead + AsyncWrite + Unpin, T: TlsConnect, { - let stream = connect_tls(stream, config.ssl_mode, tls).await?; + let stream = connect_tls(stream, config.ssl_mode, tls, has_hostname).await?; let mut stream = StartupStream { inner: Framed::new(stream, PostgresCodec), diff --git a/tokio-postgres/src/connect_tls.rs b/tokio-postgres/src/connect_tls.rs index d75dcde90..2b1229125 100644 --- a/tokio-postgres/src/connect_tls.rs +++ b/tokio-postgres/src/connect_tls.rs @@ -10,7 +10,8 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; pub async fn connect_tls( mut stream: S, mode: SslMode, - tls: Option, + tls: T, + has_hostname: bool, ) -> Result, Error> where S: AsyncRead + AsyncWrite + Unpin, @@ -18,11 +19,7 @@ where { match mode { SslMode::Disable => return Ok(MaybeTlsStream::Raw(stream)), - SslMode::Prefer - if tls - .as_ref() - .map_or(false, |tls| !tls.can_connect(ForcePrivateApi)) => - { + SslMode::Prefer if !tls.can_connect(ForcePrivateApi) => { return Ok(MaybeTlsStream::Raw(stream)) } SslMode::Prefer | SslMode::Require => {} @@ -43,8 +40,11 @@ where } } + if !has_hostname { + return Err(Error::tls("no hostname provided for TLS handshake".into())); + } + let stream = tls - .ok_or_else(|| Error::tls("no hostname provided for TLS handshake".into()))? .connect(stream) .await .map_err(|e| Error::tls(e.into()))?; From 3346858dd26b20d63eaae8f3db86773b6896b4c3 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 23 Jul 2023 09:52:56 -0400 Subject: [PATCH 082/207] Implement load balancing --- tokio-postgres/Cargo.toml | 1 + tokio-postgres/src/cancel_query.rs | 2 +- tokio-postgres/src/client.rs | 14 ++++- tokio-postgres/src/config.rs | 43 +++++++++++++ tokio-postgres/src/connect.rs | 93 +++++++++++++++++++++------- tokio-postgres/src/connect_socket.rs | 65 +++++++------------ 6 files changed, 149 insertions(+), 69 deletions(-) diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 12d8a66fd..12c4bd689 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -58,6 +58,7 @@ postgres-protocol = { version = "0.6.5", path = "../postgres-protocol" } postgres-types = { version = "0.2.4", path = "../postgres-types" } tokio = { version = "1.27", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } +rand = "0.8.5" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] socket2 = { version = "0.5", features = ["all"] } diff --git a/tokio-postgres/src/cancel_query.rs b/tokio-postgres/src/cancel_query.rs index 4a7766d60..078d4b8b6 100644 --- a/tokio-postgres/src/cancel_query.rs +++ b/tokio-postgres/src/cancel_query.rs @@ -30,7 +30,7 @@ where let has_hostname = config.hostname.is_some(); let socket = connect_socket::connect_socket( - &config.host, + &config.addr, config.port, config.connect_timeout, config.tcp_user_timeout, diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index ac486813e..2185d2146 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -1,6 +1,4 @@ use crate::codec::{BackendMessages, FrontendMessage}; -#[cfg(feature = "runtime")] -use crate::config::Host; use crate::config::SslMode; use crate::connection::{Request, RequestMessages}; use crate::copy_out::CopyOutStream; @@ -27,6 +25,8 @@ use postgres_protocol::message::{backend::Message, frontend}; use postgres_types::BorrowToSql; use std::collections::HashMap; use std::fmt; +use std::net::IpAddr; +use std::path::PathBuf; use std::sync::Arc; use std::task::{Context, Poll}; #[cfg(feature = "runtime")] @@ -153,7 +153,7 @@ impl InnerClient { #[cfg(feature = "runtime")] #[derive(Clone)] pub(crate) struct SocketConfig { - pub host: Host, + pub addr: Addr, pub hostname: Option, pub port: u16, pub connect_timeout: Option, @@ -161,6 +161,14 @@ pub(crate) struct SocketConfig { pub keepalive: Option, } +#[cfg(feature = "runtime")] +#[derive(Clone)] +pub(crate) enum Addr { + Tcp(IpAddr), + #[cfg(unix)] + Unix(PathBuf), +} + /// An asynchronous PostgreSQL client. /// /// The client is one half of what is returned when a connection is established. Users interact with the database diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index a7fa19312..87d77d35a 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -60,6 +60,16 @@ pub enum ChannelBinding { Require, } +/// Load balancing configuration. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum LoadBalanceHosts { + /// Make connection attempts to hosts in the order provided. + Disable, + /// Make connection attempts to hosts in a random order. + Random, +} + /// A host specification. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Host { @@ -129,6 +139,12 @@ pub enum Host { /// * `channel_binding` - Controls usage of channel binding in the authentication process. If set to `disable`, channel /// binding will not be used. If set to `prefer`, channel binding will be used if available, but not used otherwise. /// If set to `require`, the authentication process will fail if channel binding is not used. Defaults to `prefer`. +/// * `load_balance_hosts` - Controls the order in which the client tries to connect to the available hosts and +/// addresses. Once a connection attempt is successful no other hosts and addresses will be tried. This parameter +/// is typically used in combination with multiple host names or a DNS record that returns multiple IPs. If set to +/// `disable`, hosts and addresses will be tried in the order provided. If set to `random`, hosts will be tried +/// in a random order, and the IP addresses resolved from a hostname will also be tried in a random order. Defaults +/// to `disable`. /// /// ## Examples /// @@ -190,6 +206,7 @@ pub struct Config { pub(crate) keepalive_config: KeepaliveConfig, pub(crate) target_session_attrs: TargetSessionAttrs, pub(crate) channel_binding: ChannelBinding, + pub(crate) load_balance_hosts: LoadBalanceHosts, } impl Default for Config { @@ -222,6 +239,7 @@ impl Config { }, target_session_attrs: TargetSessionAttrs::Any, channel_binding: ChannelBinding::Prefer, + load_balance_hosts: LoadBalanceHosts::Disable, } } @@ -489,6 +507,19 @@ impl Config { self.channel_binding } + /// Sets the host load balancing behavior. + /// + /// Defaults to `disable`. + pub fn load_balance_hosts(&mut self, load_balance_hosts: LoadBalanceHosts) -> &mut Config { + self.load_balance_hosts = load_balance_hosts; + self + } + + /// Gets the host load balancing behavior. + pub fn get_load_balance_hosts(&self) -> LoadBalanceHosts { + self.load_balance_hosts + } + fn param(&mut self, key: &str, value: &str) -> Result<(), Error> { match key { "user" => { @@ -612,6 +643,18 @@ impl Config { }; self.channel_binding(channel_binding); } + "load_balance_hosts" => { + let load_balance_hosts = match value { + "disable" => LoadBalanceHosts::Disable, + "random" => LoadBalanceHosts::Random, + _ => { + return Err(Error::config_parse(Box::new(InvalidValue( + "load_balance_hosts", + )))) + } + }; + self.load_balance_hosts(load_balance_hosts); + } key => { return Err(Error::config_parse(Box::new(UnknownOption( key.to_string(), diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index 441ad1238..ca57b9cdd 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -1,12 +1,14 @@ -use crate::client::SocketConfig; -use crate::config::{Host, TargetSessionAttrs}; +use crate::client::{Addr, SocketConfig}; +use crate::config::{Host, LoadBalanceHosts, TargetSessionAttrs}; use crate::connect_raw::connect_raw; use crate::connect_socket::connect_socket; -use crate::tls::{MakeTlsConnect, TlsConnect}; +use crate::tls::MakeTlsConnect; use crate::{Client, Config, Connection, Error, SimpleQueryMessage, Socket}; use futures_util::{future, pin_mut, Future, FutureExt, Stream}; +use rand::seq::SliceRandom; use std::task::Poll; use std::{cmp, io}; +use tokio::net; pub async fn connect( mut tls: T, @@ -40,8 +42,13 @@ where return Err(Error::config("invalid number of ports".into())); } + let mut indices = (0..num_hosts).collect::>(); + if config.load_balance_hosts == LoadBalanceHosts::Random { + indices.shuffle(&mut rand::thread_rng()); + } + let mut error = None; - for i in 0..num_hosts { + for i in indices { let host = config.host.get(i); let hostaddr = config.hostaddr.get(i); let port = config @@ -59,25 +66,15 @@ where Some(Host::Unix(_)) => None, None => None, }; - let tls = tls - .make_tls_connect(hostname.as_deref().unwrap_or("")) - .map_err(|e| Error::tls(e.into()))?; // Try to use the value of hostaddr to establish the TCP connection, // fallback to host if hostaddr is not present. let addr = match hostaddr { Some(ipaddr) => Host::Tcp(ipaddr.to_string()), - None => { - if let Some(host) = host { - host.clone() - } else { - // This is unreachable. - return Err(Error::config("both host and hostaddr are empty".into())); - } - } + None => host.cloned().unwrap(), }; - match connect_once(addr, hostname, port, tls, config).await { + match connect_host(addr, hostname, port, &mut tls, config).await { Ok((client, connection)) => return Ok((client, connection)), Err(e) => error = Some(e), } @@ -86,18 +83,66 @@ where Err(error.unwrap()) } -async fn connect_once( +async fn connect_host( host: Host, hostname: Option, port: u16, - tls: T, + tls: &mut T, + config: &Config, +) -> Result<(Client, Connection), Error> +where + T: MakeTlsConnect, +{ + match host { + Host::Tcp(host) => { + let mut addrs = net::lookup_host((&*host, port)) + .await + .map_err(Error::connect)? + .collect::>(); + + if config.load_balance_hosts == LoadBalanceHosts::Random { + addrs.shuffle(&mut rand::thread_rng()); + } + + let mut last_err = None; + for addr in addrs { + match connect_once(Addr::Tcp(addr.ip()), hostname.as_deref(), port, tls, config) + .await + { + Ok(stream) => return Ok(stream), + Err(e) => { + last_err = Some(e); + continue; + } + }; + } + + Err(last_err.unwrap_or_else(|| { + Error::connect(io::Error::new( + io::ErrorKind::InvalidInput, + "could not resolve any addresses", + )) + })) + } + #[cfg(unix)] + Host::Unix(path) => { + connect_once(Addr::Unix(path), hostname.as_deref(), port, tls, config).await + } + } +} + +async fn connect_once( + addr: Addr, + hostname: Option<&str>, + port: u16, + tls: &mut T, config: &Config, ) -> Result<(Client, Connection), Error> where - T: TlsConnect, + T: MakeTlsConnect, { let socket = connect_socket( - &host, + &addr, port, config.connect_timeout, config.tcp_user_timeout, @@ -108,6 +153,10 @@ where }, ) .await?; + + let tls = tls + .make_tls_connect(hostname.unwrap_or("")) + .map_err(|e| Error::tls(e.into()))?; let has_hostname = hostname.is_some(); let (mut client, mut connection) = connect_raw(socket, tls, has_hostname, config).await?; @@ -152,8 +201,8 @@ where } client.set_socket_config(SocketConfig { - host, - hostname, + addr, + hostname: hostname.map(|s| s.to_string()), port, connect_timeout: config.connect_timeout, tcp_user_timeout: config.tcp_user_timeout, diff --git a/tokio-postgres/src/connect_socket.rs b/tokio-postgres/src/connect_socket.rs index 1204ca1ff..082cad5dc 100644 --- a/tokio-postgres/src/connect_socket.rs +++ b/tokio-postgres/src/connect_socket.rs @@ -1,17 +1,17 @@ -use crate::config::Host; +use crate::client::Addr; use crate::keepalive::KeepaliveConfig; use crate::{Error, Socket}; use socket2::{SockRef, TcpKeepalive}; use std::future::Future; use std::io; use std::time::Duration; +use tokio::net::TcpStream; #[cfg(unix)] use tokio::net::UnixStream; -use tokio::net::{self, TcpStream}; use tokio::time; pub(crate) async fn connect_socket( - host: &Host, + addr: &Addr, port: u16, connect_timeout: Option, #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] tcp_user_timeout: Option< @@ -19,53 +19,32 @@ pub(crate) async fn connect_socket( >, keepalive_config: Option<&KeepaliveConfig>, ) -> Result { - match host { - Host::Tcp(host) => { - let addrs = net::lookup_host((&**host, port)) - .await - .map_err(Error::connect)?; + match addr { + Addr::Tcp(ip) => { + let stream = + connect_with_timeout(TcpStream::connect((*ip, port)), connect_timeout).await?; - let mut last_err = None; + stream.set_nodelay(true).map_err(Error::connect)?; - for addr in addrs { - let stream = - match connect_with_timeout(TcpStream::connect(addr), connect_timeout).await { - Ok(stream) => stream, - Err(e) => { - last_err = Some(e); - continue; - } - }; - - stream.set_nodelay(true).map_err(Error::connect)?; - - let sock_ref = SockRef::from(&stream); - #[cfg(target_os = "linux")] - { - sock_ref - .set_tcp_user_timeout(tcp_user_timeout) - .map_err(Error::connect)?; - } - - if let Some(keepalive_config) = keepalive_config { - sock_ref - .set_tcp_keepalive(&TcpKeepalive::from(keepalive_config)) - .map_err(Error::connect)?; - } + let sock_ref = SockRef::from(&stream); + #[cfg(target_os = "linux")] + { + sock_ref + .set_tcp_user_timeout(tcp_user_timeout) + .map_err(Error::connect)?; + } - return Ok(Socket::new_tcp(stream)); + if let Some(keepalive_config) = keepalive_config { + sock_ref + .set_tcp_keepalive(&TcpKeepalive::from(keepalive_config)) + .map_err(Error::connect)?; } - Err(last_err.unwrap_or_else(|| { - Error::connect(io::Error::new( - io::ErrorKind::InvalidInput, - "could not resolve any addresses", - )) - })) + return Ok(Socket::new_tcp(stream)); } #[cfg(unix)] - Host::Unix(path) => { - let path = path.join(format!(".s.PGSQL.{}", port)); + Addr::Unix(dir) => { + let path = dir.join(format!(".s.PGSQL.{}", port)); let socket = connect_with_timeout(UnixStream::connect(path), connect_timeout).await?; Ok(Socket::new_unix(socket)) } From babc8562276cb51288671530045faa094ee7f35d Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 23 Jul 2023 09:55:27 -0400 Subject: [PATCH 083/207] clippy --- tokio-postgres/src/connect_socket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/src/connect_socket.rs b/tokio-postgres/src/connect_socket.rs index 082cad5dc..f27131178 100644 --- a/tokio-postgres/src/connect_socket.rs +++ b/tokio-postgres/src/connect_socket.rs @@ -40,7 +40,7 @@ pub(crate) async fn connect_socket( .map_err(Error::connect)?; } - return Ok(Socket::new_tcp(stream)); + Ok(Socket::new_tcp(stream)) } #[cfg(unix)] Addr::Unix(dir) => { From 84aed6312fb01ffa7664290b86af5e442ed8f6e9 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 23 Jul 2023 09:56:32 -0400 Subject: [PATCH 084/207] fix wasm build --- tokio-postgres/src/client.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index 2185d2146..427a05049 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -25,7 +25,9 @@ use postgres_protocol::message::{backend::Message, frontend}; use postgres_types::BorrowToSql; use std::collections::HashMap; use std::fmt; +#[cfg(feature = "runtime")] use std::net::IpAddr; +#[cfg(feature = "runtime")] use std::path::PathBuf; use std::sync::Arc; use std::task::{Context, Poll}; From 98814b86bbe1c0daac2f29ffd55c675199b1877a Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 19 Aug 2023 16:22:18 +0300 Subject: [PATCH 085/207] Set user to executing processes' user by default. This mimics the behaviour of libpq and some other libraries (see #1024). This commit uses the `whoami` crate, and thus goes as far as defaulting the user to the executing process' user name on all operating systems. --- tokio-postgres/Cargo.toml | 1 + tokio-postgres/src/config.rs | 21 +++++++++++---------- tokio-postgres/src/connect_raw.rs | 9 ++------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 12c4bd689..29cf26829 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -59,6 +59,7 @@ postgres-types = { version = "0.2.4", path = "../postgres-types" } tokio = { version = "1.27", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } rand = "0.8.5" +whoami = "1.4.1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] socket2 = { version = "0.5", features = ["all"] } diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 87d77d35a..a94667dc9 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -93,7 +93,7 @@ pub enum Host { /// /// ## Keys /// -/// * `user` - The username to authenticate with. Required. +/// * `user` - The username to authenticate with. Defaults to the user executing this process. /// * `password` - The password to authenticate with. /// * `dbname` - The name of the database to connect to. Defaults to the username. /// * `options` - Command line options used to configure the server. @@ -190,7 +190,7 @@ pub enum Host { /// ``` #[derive(Clone, PartialEq, Eq)] pub struct Config { - pub(crate) user: Option, + user: String, pub(crate) password: Option>, pub(crate) dbname: Option, pub(crate) options: Option, @@ -219,7 +219,7 @@ impl Config { /// Creates a new configuration. pub fn new() -> Config { Config { - user: None, + user: whoami::username(), password: None, dbname: None, options: None, @@ -245,16 +245,17 @@ impl Config { /// Sets the user to authenticate with. /// - /// Required. + /// If the user is not set, then this defaults to the user executing this process. pub fn user(&mut self, user: &str) -> &mut Config { - self.user = Some(user.to_string()); + self.user = user.to_string(); self } - /// Gets the user to authenticate with, if one has been configured with - /// the `user` method. - pub fn get_user(&self) -> Option<&str> { - self.user.as_deref() + /// Gets the user to authenticate with. + /// If no user has been configured with the [`user`](Config::user) method, + /// then this defaults to the user executing this process. + pub fn get_user(&self) -> &str { + &self.user } /// Sets the password to authenticate with. @@ -1124,7 +1125,7 @@ mod tests { fn test_simple_parsing() { let s = "user=pass_user dbname=postgres host=host1,host2 hostaddr=127.0.0.1,127.0.0.2 port=26257"; let config = s.parse::().unwrap(); - assert_eq!(Some("pass_user"), config.get_user()); + assert_eq!("pass_user", config.get_user()); assert_eq!(Some("postgres"), config.get_dbname()); assert_eq!( [ diff --git a/tokio-postgres/src/connect_raw.rs b/tokio-postgres/src/connect_raw.rs index 254ca9f0c..bb511c47e 100644 --- a/tokio-postgres/src/connect_raw.rs +++ b/tokio-postgres/src/connect_raw.rs @@ -113,9 +113,7 @@ where T: AsyncRead + AsyncWrite + Unpin, { let mut params = vec![("client_encoding", "UTF8")]; - if let Some(user) = &config.user { - params.push(("user", &**user)); - } + params.push(("user", config.get_user())); if let Some(dbname) = &config.dbname { params.push(("database", &**dbname)); } @@ -158,10 +156,7 @@ where Some(Message::AuthenticationMd5Password(body)) => { can_skip_channel_binding(config)?; - let user = config - .user - .as_ref() - .ok_or_else(|| Error::config("user missing".into()))?; + let user = config.get_user(); let pass = config .password .as_ref() From 4c4059a63d273b94badf1c90998ffaa7c67091c0 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 19 Aug 2023 18:48:57 +0300 Subject: [PATCH 086/207] Propagate changes from `tokio-postgres` to `postgres`. --- postgres/src/config.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index 2a8e63862..0e1fbde62 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -29,7 +29,7 @@ use tokio_postgres::{Error, Socket}; /// /// ## Keys /// -/// * `user` - The username to authenticate with. Required. +/// * `user` - The username to authenticate with. Defaults to the user executing this process. /// * `password` - The password to authenticate with. /// * `dbname` - The name of the database to connect to. Defaults to the username. /// * `options` - Command line options used to configure the server. @@ -143,15 +143,16 @@ impl Config { /// Sets the user to authenticate with. /// - /// Required. + /// If the user is not set, then this defaults to the user executing this process. pub fn user(&mut self, user: &str) -> &mut Config { self.config.user(user); self } - /// Gets the user to authenticate with, if one has been configured with - /// the `user` method. - pub fn get_user(&self) -> Option<&str> { + /// Gets the user to authenticate with. + /// If no user has been configured with the [`user`](Config::user) method, + /// then this defaults to the user executing this process. + pub fn get_user(&self) -> &str { self.config.get_user() } From 7a5b19a7861d784a0a743f89447d4c732ac44b90 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 19 Aug 2023 19:09:00 +0300 Subject: [PATCH 087/207] Update Rust version in CI to 1.67.0. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ebe0f600f..9a669a40f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,7 @@ jobs: - run: docker compose up -d - uses: sfackler/actions/rustup@master with: - version: 1.65.0 + version: 1.67.0 - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - uses: actions/cache@v3 From a4543783707cc2fdbba3db4bfe1fc6168582de7e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 Aug 2023 19:53:26 -0400 Subject: [PATCH 088/207] Restore back compat --- postgres/src/config.rs | 7 +++++-- tokio-postgres/src/config.rs | 15 +++++++++------ tokio-postgres/src/connect_raw.rs | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index 0e1fbde62..1839c9cb3 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -150,9 +150,12 @@ impl Config { } /// Gets the user to authenticate with. + /// /// If no user has been configured with the [`user`](Config::user) method, - /// then this defaults to the user executing this process. - pub fn get_user(&self) -> &str { + /// then this defaults to the user executing this process. It always + /// returns `Some`. + // FIXME remove option + pub fn get_user(&self) -> Option<&str> { self.config.get_user() } diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index a94667dc9..0da5fc689 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -190,7 +190,7 @@ pub enum Host { /// ``` #[derive(Clone, PartialEq, Eq)] pub struct Config { - user: String, + pub(crate) user: String, pub(crate) password: Option>, pub(crate) dbname: Option, pub(crate) options: Option, @@ -245,17 +245,20 @@ impl Config { /// Sets the user to authenticate with. /// - /// If the user is not set, then this defaults to the user executing this process. + /// Defaults to the user executing this process. pub fn user(&mut self, user: &str) -> &mut Config { self.user = user.to_string(); self } /// Gets the user to authenticate with. + /// /// If no user has been configured with the [`user`](Config::user) method, - /// then this defaults to the user executing this process. - pub fn get_user(&self) -> &str { - &self.user + /// then this defaults to the user executing this process. It always + /// returns `Some`. + // FIXME remove option + pub fn get_user(&self) -> Option<&str> { + Some(&self.user) } /// Sets the password to authenticate with. @@ -1125,7 +1128,7 @@ mod tests { fn test_simple_parsing() { let s = "user=pass_user dbname=postgres host=host1,host2 hostaddr=127.0.0.1,127.0.0.2 port=26257"; let config = s.parse::().unwrap(); - assert_eq!("pass_user", config.get_user()); + assert_eq!(Some("pass_user"), config.get_user()); assert_eq!(Some("postgres"), config.get_dbname()); assert_eq!( [ diff --git a/tokio-postgres/src/connect_raw.rs b/tokio-postgres/src/connect_raw.rs index bb511c47e..11cc48ef8 100644 --- a/tokio-postgres/src/connect_raw.rs +++ b/tokio-postgres/src/connect_raw.rs @@ -113,7 +113,7 @@ where T: AsyncRead + AsyncWrite + Unpin, { let mut params = vec![("client_encoding", "UTF8")]; - params.push(("user", config.get_user())); + params.push(("user", &config.user)); if let Some(dbname) = &config.dbname { params.push(("database", &**dbname)); } @@ -156,7 +156,7 @@ where Some(Message::AuthenticationMd5Password(body)) => { can_skip_channel_binding(config)?; - let user = config.get_user(); + let user = &config.user; let pass = config .password .as_ref() From 496f46c8f5e8e76e0b148c7ef57dbccc11778597 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 Aug 2023 20:04:18 -0400 Subject: [PATCH 089/207] Release postgres-protocol v0.6.6 --- postgres-protocol/CHANGELOG.md | 6 ++++++ postgres-protocol/Cargo.toml | 2 +- postgres-protocol/src/lib.rs | 1 - postgres-types/Cargo.toml | 2 +- tokio-postgres/Cargo.toml | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/postgres-protocol/CHANGELOG.md b/postgres-protocol/CHANGELOG.md index 034fd637c..1c371675c 100644 --- a/postgres-protocol/CHANGELOG.md +++ b/postgres-protocol/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.6.6 -2023-08-19 + +### Added + +* Added the `js` feature for WASM support. + ## v0.6.5 - 2023-03-27 ### Added diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index ad609f6fa..b44994811 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-protocol" -version = "0.6.5" +version = "0.6.6" authors = ["Steven Fackler "] edition = "2018" description = "Low level Postgres protocol APIs" diff --git a/postgres-protocol/src/lib.rs b/postgres-protocol/src/lib.rs index 8b6ff508d..83d9bf55c 100644 --- a/postgres-protocol/src/lib.rs +++ b/postgres-protocol/src/lib.rs @@ -9,7 +9,6 @@ //! //! This library assumes that the `client_encoding` backend parameter has been //! set to `UTF8`. It will most likely not behave properly if that is not the case. -#![doc(html_root_url = "https://docs.rs/postgres-protocol/0.6")] #![warn(missing_docs, rust_2018_idioms, clippy::all)] use byteorder::{BigEndian, ByteOrder}; diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index 35cdd6e7b..686d0036d 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -30,7 +30,7 @@ with-time-0_3 = ["time-03"] [dependencies] bytes = "1.0" fallible-iterator = "0.2" -postgres-protocol = { version = "0.6.4", path = "../postgres-protocol" } +postgres-protocol = { version = "0.6.5", path = "../postgres-protocol" } postgres-derive = { version = "0.4.2", optional = true, path = "../postgres-derive" } array-init = { version = "2", optional = true } diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 29cf26829..f9f49da3e 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -54,7 +54,7 @@ parking_lot = "0.12" percent-encoding = "2.0" pin-project-lite = "0.2" phf = "0.11" -postgres-protocol = { version = "0.6.5", path = "../postgres-protocol" } +postgres-protocol = { version = "0.6.6", path = "../postgres-protocol" } postgres-types = { version = "0.2.4", path = "../postgres-types" } tokio = { version = "1.27", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } From 43e15690f492f3ae8088677fd8d5df18f73b3e85 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 Aug 2023 20:11:35 -0400 Subject: [PATCH 090/207] Release postgres-derive v0.4.5 --- postgres-derive/CHANGELOG.md | 7 +++++++ postgres-derive/Cargo.toml | 2 +- postgres-types/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/postgres-derive/CHANGELOG.md b/postgres-derive/CHANGELOG.md index 22714acc2..b0075fa8e 100644 --- a/postgres-derive/CHANGELOG.md +++ b/postgres-derive/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## v0.4.5 - 2023-08-19 + +### Added + +* Added a `rename_all` option for enum and struct derives. +* Added an `allow_mismatch` option to disable strict enum variant checks against the Postgres type. + ## v0.4.4 - 2023-03-27 ### Changed diff --git a/postgres-derive/Cargo.toml b/postgres-derive/Cargo.toml index 78bec3d41..51ebb5663 100644 --- a/postgres-derive/Cargo.toml +++ b/postgres-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-derive" -version = "0.4.4" +version = "0.4.5" authors = ["Steven Fackler "] license = "MIT/Apache-2.0" edition = "2018" diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index 686d0036d..15de00702 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -31,7 +31,7 @@ with-time-0_3 = ["time-03"] bytes = "1.0" fallible-iterator = "0.2" postgres-protocol = { version = "0.6.5", path = "../postgres-protocol" } -postgres-derive = { version = "0.4.2", optional = true, path = "../postgres-derive" } +postgres-derive = { version = "0.4.5", optional = true, path = "../postgres-derive" } array-init = { version = "2", optional = true } bit-vec-06 = { version = "0.6", package = "bit-vec", optional = true } From 6f7ab44d5bc8548a4e7fb69d46d3b85a14101144 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 Aug 2023 20:14:01 -0400 Subject: [PATCH 091/207] Release postgres-types v0.2.6 --- postgres-types/CHANGELOG.md | 15 +++++++++++++-- postgres-types/Cargo.toml | 2 +- postgres-types/src/lib.rs | 1 - 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/postgres-types/CHANGELOG.md b/postgres-types/CHANGELOG.md index 0f42f3495..72a1cbb6a 100644 --- a/postgres-types/CHANGELOG.md +++ b/postgres-types/CHANGELOG.md @@ -1,14 +1,25 @@ # Change Log +## v0.2.6 - 2023-08-19 + +### Fixed + +* Fixed serialization to `OIDVECTOR` and `INT2VECTOR`. + +### Added + +* Removed the `'static` requirement for the `impl BorrowToSql for Box`. +* Added a `ToSql` implementation for `Cow<[u8]>`. + ## v0.2.5 - 2023-03-27 -## Added +### Added * Added support for multi-range types. ## v0.2.4 - 2022-08-20 -## Added +### Added * Added `ToSql` and `FromSql` implementations for `Box<[T]>`. * Added `ToSql` and `FromSql` implementations for `[u8; N]` via the `array-impls` feature. diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index 15de00702..193d159a1 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-types" -version = "0.2.5" +version = "0.2.6" authors = ["Steven Fackler "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index d27adfe0e..52b5c773a 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -174,7 +174,6 @@ //! Meh, //! } //! ``` -#![doc(html_root_url = "https://docs.rs/postgres-types/0.2")] #![warn(clippy::all, rust_2018_idioms, missing_docs)] use fallible_iterator::FallibleIterator; use postgres_protocol::types::{self, ArrayDimension}; From 3d0a593ea610fb51b25a34087131470c94e3fe58 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 Aug 2023 20:20:13 -0400 Subject: [PATCH 092/207] Release tokio-postgres v0.7.9 --- tokio-postgres/CHANGELOG.md | 13 +++++++++++++ tokio-postgres/Cargo.toml | 4 ++-- tokio-postgres/src/lib.rs | 1 - 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index 3345a1d43..41a1a65d1 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## v0.7.9 + +## Fixed + +* Fixed builds on OpenBSD. + +## Added + +* Added the `js` feature for WASM support. +* Added support for the `hostaddr` config option to bypass DNS lookups. +* Added support for the `load_balance_hosts` config option to randomize connection ordering. +* The `user` config option now defaults to the executing process's user. + ## v0.7.8 ## Added diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index f9f49da3e..3b33cc8f6 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-postgres" -version = "0.7.8" +version = "0.7.9" authors = ["Steven Fackler "] edition = "2018" license = "MIT/Apache-2.0" @@ -55,7 +55,7 @@ percent-encoding = "2.0" pin-project-lite = "0.2" phf = "0.11" postgres-protocol = { version = "0.6.6", path = "../postgres-protocol" } -postgres-types = { version = "0.2.4", path = "../postgres-types" } +postgres-types = { version = "0.2.5", path = "../postgres-types" } tokio = { version = "1.27", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } rand = "0.8.5" diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs index 2bb410187..ff8e93ddc 100644 --- a/tokio-postgres/src/lib.rs +++ b/tokio-postgres/src/lib.rs @@ -116,7 +116,6 @@ //! | `with-uuid-1` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 1.0 | no | //! | `with-time-0_2` | Enable support for the 0.2 version of the `time` crate. | [time](https://crates.io/crates/time/0.2.0) 0.2 | no | //! | `with-time-0_3` | Enable support for the 0.3 version of the `time` crate. | [time](https://crates.io/crates/time/0.3.0) 0.3 | no | -#![doc(html_root_url = "https://docs.rs/tokio-postgres/0.7")] #![warn(rust_2018_idioms, clippy::all, missing_docs)] pub use crate::cancel_token::CancelToken; From e08a38f9f6f06a67d699209d54097fa8a567a578 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 Aug 2023 20:33:21 -0400 Subject: [PATCH 093/207] sync postgres config up with tokio-postgres --- postgres/src/config.rs | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index 1839c9cb3..0f936fdc4 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -13,7 +13,9 @@ use std::sync::Arc; use std::time::Duration; use tokio::runtime; #[doc(inline)] -pub use tokio_postgres::config::{ChannelBinding, Host, SslMode, TargetSessionAttrs}; +pub use tokio_postgres::config::{ + ChannelBinding, Host, LoadBalanceHosts, SslMode, TargetSessionAttrs, +}; use tokio_postgres::error::DbError; use tokio_postgres::tls::{MakeTlsConnect, TlsConnect}; use tokio_postgres::{Error, Socket}; @@ -43,9 +45,9 @@ use tokio_postgres::{Error, Socket}; /// * `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, -/// - or if host specifies an IP address, that value will be used directly. +/// or if host specifies an IP address, that value will be used directly. /// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications -/// with time constraints. However, a host name is required for verify-full SSL certificate verification. +/// with time constraints. However, a host name is required for TLS certificate verification. /// Specifically: /// * If `hostaddr` is specified without `host`, the value for `hostaddr` gives the server network address. /// The connection attempt will fail if the authentication method requires a host name; @@ -72,6 +74,15 @@ use tokio_postgres::{Error, Socket}; /// * `target_session_attrs` - Specifies requirements of the session. If set to `read-write`, the client will check that /// the `transaction_read_write` session parameter is set to `on`. This can be used to connect to the primary server /// in a database cluster as opposed to the secondary read-only mirrors. Defaults to `all`. +/// * `channel_binding` - Controls usage of channel binding in the authentication process. If set to `disable`, channel +/// binding will not be used. If set to `prefer`, channel binding will be used if available, but not used otherwise. +/// If set to `require`, the authentication process will fail if channel binding is not used. Defaults to `prefer`. +/// * `load_balance_hosts` - Controls the order in which the client tries to connect to the available hosts and +/// addresses. Once a connection attempt is successful no other hosts and addresses will be tried. This parameter +/// is typically used in combination with multiple host names or a DNS record that returns multiple IPs. If set to +/// `disable`, hosts and addresses will be tried in the order provided. If set to `random`, hosts will be tried +/// in a random order, and the IP addresses resolved from a hostname will also be tried in a random order. Defaults +/// to `disable`. /// /// ## Examples /// @@ -80,7 +91,7 @@ use tokio_postgres::{Error, Socket}; /// ``` /// /// ```not_rust -/// host=/var/run/postgresql,localhost port=1234 user=postgres password='password with spaces' +/// host=/var/lib/postgresql,localhost port=1234 user=postgres password='password with spaces' /// ``` /// /// ```not_rust @@ -94,7 +105,7 @@ use tokio_postgres::{Error, Socket}; /// # Url /// /// This format resembles a URL with a scheme of either `postgres://` or `postgresql://`. All components are optional, -/// and the format accept query parameters for all of the key-value pairs described in the section above. Multiple +/// and the format accepts query parameters for all of the key-value pairs described in the section above. Multiple /// host/port pairs can be comma-separated. Unix socket paths in the host section of the URL should be percent-encoded, /// as the path component of the URL specifies the database name. /// @@ -105,7 +116,7 @@ use tokio_postgres::{Error, Socket}; /// ``` /// /// ```not_rust -/// postgresql://user:password@%2Fvar%2Frun%2Fpostgresql/mydb?connect_timeout=10 +/// postgresql://user:password@%2Fvar%2Flib%2Fpostgresql/mydb?connect_timeout=10 /// ``` /// /// ```not_rust @@ -113,7 +124,7 @@ use tokio_postgres::{Error, Socket}; /// ``` /// /// ```not_rust -/// postgresql:///mydb?user=user&host=/var/run/postgresql +/// postgresql:///mydb?user=user&host=/var/lib/postgresql /// ``` #[derive(Clone)] pub struct Config { @@ -396,6 +407,19 @@ impl Config { self.config.get_channel_binding() } + /// Sets the host load balancing behavior. + /// + /// Defaults to `disable`. + pub fn load_balance_hosts(&mut self, load_balance_hosts: LoadBalanceHosts) -> &mut Config { + self.config.load_balance_hosts(load_balance_hosts); + self + } + + /// Gets the host load balancing behavior. + pub fn get_load_balance_hosts(&self) -> LoadBalanceHosts { + self.config.get_load_balance_hosts() + } + /// Sets the notice callback. /// /// This callback will be invoked with the contents of every From f45527fe5f4f566328973097511a33d771d3f300 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 Aug 2023 20:34:02 -0400 Subject: [PATCH 094/207] remove bogus docs --- postgres/src/config.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index 0f936fdc4..f83244b2e 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -1,6 +1,4 @@ //! Connection configuration. -//! -//! Requires the `runtime` Cargo feature (enabled by default). use crate::connection::Connection; use crate::Client; From 75cc986d8c40024eca45139edc6c366231d147ea Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 19 Aug 2023 20:37:16 -0400 Subject: [PATCH 095/207] Release postgres v0.19.6 --- postgres/CHANGELOG.md | 14 +++++++++++--- postgres/Cargo.toml | 8 +++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/postgres/CHANGELOG.md b/postgres/CHANGELOG.md index b8263a04a..fe9e8dbe8 100644 --- a/postgres/CHANGELOG.md +++ b/postgres/CHANGELOG.md @@ -1,20 +1,28 @@ # Change Log +## v0.19.6 - 2023-08-19 + +### Added + +* Added support for the `hostaddr` config option to bypass DNS lookups. +* Added support for the `load_balance_hosts` config option to randomize connection ordering. +* The `user` config option now defaults to the executing process's user. + ## v0.19.5 - 2023-03-27 -## Added +### Added * Added `keepalives_interval` and `keepalives_retries` config options. * Added the `tcp_user_timeout` config option. * Added `RowIter::rows_affected`. -## Changed +### Changed * Passing an incorrect number of parameters to a query method now returns an error instead of panicking. ## v0.19.4 - 2022-08-21 -## Added +### Added * Added `ToSql` and `FromSql` implementations for `[u8; N]` via the `array-impls` feature. * Added support for `smol_str` 0.1 via the `with-smol_str-01` feature. diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index 044bb91e1..ff626f86c 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres" -version = "0.19.5" +version = "0.19.6" authors = ["Steven Fackler "] edition = "2018" license = "MIT/Apache-2.0" @@ -39,11 +39,9 @@ with-time-0_3 = ["tokio-postgres/with-time-0_3"] bytes = "1.0" fallible-iterator = "0.2" futures-util = { version = "0.3.14", features = ["sink"] } -tokio-postgres = { version = "0.7.8", path = "../tokio-postgres" } - -tokio = { version = "1.0", features = ["rt", "time"] } log = "0.4" +tokio-postgres = { version = "0.7.9", path = "../tokio-postgres" } +tokio = { version = "1.0", features = ["rt", "time"] } [dev-dependencies] criterion = "0.5" -tokio = { version = "1.0", features = ["rt-multi-thread"] } From cb609be758f3fb5af537f04b584a2ee0cebd5e79 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 25 Aug 2023 13:31:22 -0400 Subject: [PATCH 096/207] Defer username default --- postgres/src/config.rs | 8 ++------ tokio-postgres/src/config.rs | 16 ++++++---------- tokio-postgres/src/connect_raw.rs | 21 +++++++++++++++------ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/postgres/src/config.rs b/postgres/src/config.rs index f83244b2e..a32ddc78e 100644 --- a/postgres/src/config.rs +++ b/postgres/src/config.rs @@ -158,12 +158,8 @@ impl Config { self } - /// Gets the user to authenticate with. - /// - /// If no user has been configured with the [`user`](Config::user) method, - /// then this defaults to the user executing this process. It always - /// returns `Some`. - // FIXME remove option + /// Gets the user to authenticate with, if one has been configured with + /// the `user` method. pub fn get_user(&self) -> Option<&str> { self.config.get_user() } diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index 0da5fc689..b178eac80 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -190,7 +190,7 @@ pub enum Host { /// ``` #[derive(Clone, PartialEq, Eq)] pub struct Config { - pub(crate) user: String, + pub(crate) user: Option, pub(crate) password: Option>, pub(crate) dbname: Option, pub(crate) options: Option, @@ -219,7 +219,7 @@ impl Config { /// Creates a new configuration. pub fn new() -> Config { Config { - user: whoami::username(), + user: None, password: None, dbname: None, options: None, @@ -247,18 +247,14 @@ impl Config { /// /// Defaults to the user executing this process. pub fn user(&mut self, user: &str) -> &mut Config { - self.user = user.to_string(); + self.user = Some(user.to_string()); self } - /// Gets the user to authenticate with. - /// - /// If no user has been configured with the [`user`](Config::user) method, - /// then this defaults to the user executing this process. It always - /// returns `Some`. - // FIXME remove option + /// Gets the user to authenticate with, if one has been configured with + /// the `user` method. pub fn get_user(&self) -> Option<&str> { - Some(&self.user) + self.user.as_deref() } /// Sets the password to authenticate with. diff --git a/tokio-postgres/src/connect_raw.rs b/tokio-postgres/src/connect_raw.rs index 11cc48ef8..f19bb50c4 100644 --- a/tokio-postgres/src/connect_raw.rs +++ b/tokio-postgres/src/connect_raw.rs @@ -96,8 +96,10 @@ where delayed: VecDeque::new(), }; - startup(&mut stream, config).await?; - authenticate(&mut stream, config).await?; + let user = config.user.clone().unwrap_or_else(whoami::username); + + startup(&mut stream, config, &user).await?; + authenticate(&mut stream, config, &user).await?; let (process_id, secret_key, parameters) = read_info(&mut stream).await?; let (sender, receiver) = mpsc::unbounded(); @@ -107,13 +109,17 @@ where Ok((client, connection)) } -async fn startup(stream: &mut StartupStream, config: &Config) -> Result<(), Error> +async fn startup( + stream: &mut StartupStream, + config: &Config, + user: &str, +) -> Result<(), Error> where S: AsyncRead + AsyncWrite + Unpin, T: AsyncRead + AsyncWrite + Unpin, { let mut params = vec![("client_encoding", "UTF8")]; - params.push(("user", &config.user)); + params.push(("user", user)); if let Some(dbname) = &config.dbname { params.push(("database", &**dbname)); } @@ -133,7 +139,11 @@ where .map_err(Error::io) } -async fn authenticate(stream: &mut StartupStream, config: &Config) -> Result<(), Error> +async fn authenticate( + stream: &mut StartupStream, + config: &Config, + user: &str, +) -> Result<(), Error> where S: AsyncRead + AsyncWrite + Unpin, T: TlsStream + Unpin, @@ -156,7 +166,6 @@ where Some(Message::AuthenticationMd5Password(body)) => { can_skip_channel_binding(config)?; - let user = &config.user; let pass = config .password .as_ref() From b411e5c3cb71d43fc9249b5d3ca38a7213470069 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 25 Aug 2023 13:35:48 -0400 Subject: [PATCH 097/207] clippy --- postgres-protocol/src/types/test.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/postgres-protocol/src/types/test.rs b/postgres-protocol/src/types/test.rs index 6f1851fc2..3e33b08f0 100644 --- a/postgres-protocol/src/types/test.rs +++ b/postgres-protocol/src/types/test.rs @@ -174,7 +174,7 @@ fn ltree_str() { let mut query = vec![1u8]; query.extend_from_slice("A.B.C".as_bytes()); - assert!(matches!(ltree_from_sql(query.as_slice()), Ok(_))) + assert!(ltree_from_sql(query.as_slice()).is_ok()) } #[test] @@ -182,7 +182,7 @@ fn ltree_wrong_version() { let mut query = vec![2u8]; query.extend_from_slice("A.B.C".as_bytes()); - assert!(matches!(ltree_from_sql(query.as_slice()), Err(_))) + assert!(ltree_from_sql(query.as_slice()).is_err()) } #[test] @@ -202,7 +202,7 @@ fn lquery_str() { let mut query = vec![1u8]; query.extend_from_slice("A.B.C".as_bytes()); - assert!(matches!(lquery_from_sql(query.as_slice()), Ok(_))) + assert!(lquery_from_sql(query.as_slice()).is_ok()) } #[test] @@ -210,7 +210,7 @@ fn lquery_wrong_version() { let mut query = vec![2u8]; query.extend_from_slice("A.B.C".as_bytes()); - assert!(matches!(lquery_from_sql(query.as_slice()), Err(_))) + assert!(lquery_from_sql(query.as_slice()).is_err()) } #[test] @@ -230,7 +230,7 @@ fn ltxtquery_str() { let mut query = vec![1u8]; query.extend_from_slice("a & b*".as_bytes()); - assert!(matches!(ltree_from_sql(query.as_slice()), Ok(_))) + assert!(ltree_from_sql(query.as_slice()).is_ok()) } #[test] @@ -238,5 +238,5 @@ fn ltxtquery_wrong_version() { let mut query = vec![2u8]; query.extend_from_slice("a & b*".as_bytes()); - assert!(matches!(ltree_from_sql(query.as_slice()), Err(_))) + assert!(ltree_from_sql(query.as_slice()).is_err()) } From 016e9a3b8557c267f650090e1501d5efd00de908 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 25 Aug 2023 13:40:01 -0400 Subject: [PATCH 098/207] avoid a silly clone --- tokio-postgres/src/connect_raw.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tokio-postgres/src/connect_raw.rs b/tokio-postgres/src/connect_raw.rs index f19bb50c4..19be9eb01 100644 --- a/tokio-postgres/src/connect_raw.rs +++ b/tokio-postgres/src/connect_raw.rs @@ -13,6 +13,7 @@ use postgres_protocol::authentication::sasl; use postgres_protocol::authentication::sasl::ScramSha256; use postgres_protocol::message::backend::{AuthenticationSaslBody, Message}; use postgres_protocol::message::frontend; +use std::borrow::Cow; use std::collections::{HashMap, VecDeque}; use std::io; use std::pin::Pin; @@ -96,7 +97,10 @@ where delayed: VecDeque::new(), }; - let user = config.user.clone().unwrap_or_else(whoami::username); + let user = config + .user + .as_deref() + .map_or_else(|| Cow::Owned(whoami::username()), Cow::Borrowed); startup(&mut stream, config, &user).await?; authenticate(&mut stream, config, &user).await?; From 234e20bb000ccf17d08341bd66e48d1105c3960a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 25 Aug 2023 13:40:40 -0400 Subject: [PATCH 099/207] bump ci version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a669a40f..008158fb0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,7 @@ jobs: - run: docker compose up -d - uses: sfackler/actions/rustup@master with: - version: 1.67.0 + version: 1.70.0 - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - uses: actions/cache@v3 From c50fcbd9fb6f0df53d2300fb429af1c6c128007f Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 25 Aug 2023 13:45:34 -0400 Subject: [PATCH 100/207] Release tokio-postgres v0.7.10 --- tokio-postgres/CHANGELOG.md | 6 ++++++ tokio-postgres/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index 41a1a65d1..2bee9a1c4 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.7.10 + +## Fixed + +* Defered default username lookup to avoid regressing `Config` behavior. + ## v0.7.9 ## Fixed diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 3b33cc8f6..ec5e3cbec 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-postgres" -version = "0.7.9" +version = "0.7.10" authors = ["Steven Fackler "] edition = "2018" license = "MIT/Apache-2.0" From c5ff8cfd86e897b7c197f52684a37a4f17cecb75 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 25 Aug 2023 13:48:08 -0400 Subject: [PATCH 101/207] Release postgres v0.19.7 --- postgres/CHANGELOG.md | 6 ++++++ postgres/Cargo.toml | 4 ++-- tokio-postgres/CHANGELOG.md | 6 +++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/postgres/CHANGELOG.md b/postgres/CHANGELOG.md index fe9e8dbe8..7f856b5ac 100644 --- a/postgres/CHANGELOG.md +++ b/postgres/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.19.7 - 2023-08-25 + +## Fixed + +* Defered default username lookup to avoid regressing `Config` behavior. + ## v0.19.6 - 2023-08-19 ### Added diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index ff626f86c..18406da9f 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres" -version = "0.19.6" +version = "0.19.7" authors = ["Steven Fackler "] edition = "2018" license = "MIT/Apache-2.0" @@ -40,7 +40,7 @@ bytes = "1.0" fallible-iterator = "0.2" futures-util = { version = "0.3.14", features = ["sink"] } log = "0.4" -tokio-postgres = { version = "0.7.9", path = "../tokio-postgres" } +tokio-postgres = { version = "0.7.10", path = "../tokio-postgres" } tokio = { version = "1.0", features = ["rt", "time"] } [dev-dependencies] diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index 2bee9a1c4..75448d130 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -1,12 +1,12 @@ # Change Log -## v0.7.10 +## v0.7.10 - 2023-08-25 ## Fixed * Defered default username lookup to avoid regressing `Config` behavior. -## v0.7.9 +## v0.7.9 - 2023-08-19 ## Fixed @@ -19,7 +19,7 @@ * Added support for the `load_balance_hosts` config option to randomize connection ordering. * The `user` config option now defaults to the executing process's user. -## v0.7.8 +## v0.7.8 - 2023-05-27 ## Added From b1306a4a74317ac142ae9b93445360e9597380ec Mon Sep 17 00:00:00 2001 From: ds-cbo <82801887+ds-cbo@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:31:41 +0200 Subject: [PATCH 102/207] remove rustc-serialize dependency --- postgres-types/Cargo.toml | 4 +++- postgres/src/lib.rs | 2 +- tokio-postgres/CHANGELOG.md | 6 ++++++ tokio-postgres/Cargo.toml | 3 +-- tokio-postgres/src/lib.rs | 2 +- tokio-postgres/tests/test/types/eui48_04.rs | 18 ------------------ tokio-postgres/tests/test/types/mod.rs | 2 -- 7 files changed, 12 insertions(+), 25 deletions(-) delete mode 100644 tokio-postgres/tests/test/types/eui48_04.rs diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index 193d159a1..cfd083637 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -39,8 +39,10 @@ chrono-04 = { version = "0.4.16", package = "chrono", default-features = false, "clock", ], optional = true } cidr-02 = { version = "0.2", 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 } -eui48-1 = { version = "1.0", package = "eui48", optional = true } +eui48-1 = { version = "1.0", package = "eui48", optional = true, default-features = false } geo-types-06 = { version = "0.6", package = "geo-types", optional = true } geo-types-0_7 = { version = "0.7", package = "geo-types", optional = true } serde-1 = { version = "1.0", package = "serde", optional = true } diff --git a/postgres/src/lib.rs b/postgres/src/lib.rs index fbe85cbde..ddf1609ad 100644 --- a/postgres/src/lib.rs +++ b/postgres/src/lib.rs @@ -55,7 +55,7 @@ //! | ------- | ----------- | ------------------ | ------- | //! | `with-bit-vec-0_6` | Enable support for the `bit-vec` crate. | [bit-vec](https://crates.io/crates/bit-vec) 0.6 | no | //! | `with-chrono-0_4` | Enable support for the `chrono` crate. | [chrono](https://crates.io/crates/chrono) 0.4 | no | -//! | `with-eui48-0_4` | Enable support for the 0.4 version of the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 0.4 | no | +//! | `with-eui48-0_4` | Enable support for the 0.4 version of the `eui48` crate. This is deprecated and will be removed. | [eui48](https://crates.io/crates/eui48) 0.4 | no | //! | `with-eui48-1` | Enable support for the 1.0 version of the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 1.0 | no | //! | `with-geo-types-0_6` | Enable support for the 0.6 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.6.0) 0.6 | no | //! | `with-geo-types-0_7` | Enable support for the 0.7 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.7.0) 0.7 | no | diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index 75448d130..bd076eef9 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## Unreleased + +* Disable `rustc-serialize` compatibility of `eui48-1` dependency +* Remove tests for `eui48-04` + + ## v0.7.10 - 2023-08-25 ## Fixed diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index ec5e3cbec..bb58eb2d9 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -78,8 +78,7 @@ tokio = { version = "1.0", features = [ bit-vec-06 = { version = "0.6", package = "bit-vec" } chrono-04 = { version = "0.4", package = "chrono", default-features = false } -eui48-04 = { version = "0.4", package = "eui48" } -eui48-1 = { version = "1.0", package = "eui48" } +eui48-1 = { version = "1.0", package = "eui48", default-features = false } geo-types-06 = { version = "0.6", package = "geo-types" } geo-types-07 = { version = "0.7", package = "geo-types" } serde-1 = { version = "1.0", package = "serde" } diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs index ff8e93ddc..2973d33b0 100644 --- a/tokio-postgres/src/lib.rs +++ b/tokio-postgres/src/lib.rs @@ -107,7 +107,7 @@ //! | `array-impls` | Enables `ToSql` and `FromSql` trait impls for arrays | - | no | //! | `with-bit-vec-0_6` | Enable support for the `bit-vec` crate. | [bit-vec](https://crates.io/crates/bit-vec) 0.6 | no | //! | `with-chrono-0_4` | Enable support for the `chrono` crate. | [chrono](https://crates.io/crates/chrono) 0.4 | no | -//! | `with-eui48-0_4` | Enable support for the 0.4 version of the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 0.4 | no | +//! | `with-eui48-0_4` | Enable support for the 0.4 version of the `eui48` crate. This is deprecated and will be removed. | [eui48](https://crates.io/crates/eui48) 0.4 | no | //! | `with-eui48-1` | Enable support for the 1.0 version of the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 1.0 | no | //! | `with-geo-types-0_6` | Enable support for the 0.6 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.6.0) 0.6 | no | //! | `with-geo-types-0_7` | Enable support for the 0.7 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.7.0) 0.7 | no | diff --git a/tokio-postgres/tests/test/types/eui48_04.rs b/tokio-postgres/tests/test/types/eui48_04.rs deleted file mode 100644 index 074faa37e..000000000 --- a/tokio-postgres/tests/test/types/eui48_04.rs +++ /dev/null @@ -1,18 +0,0 @@ -use eui48_04::MacAddress; - -use crate::types::test_type; - -#[tokio::test] -async fn test_eui48_params() { - test_type( - "MACADDR", - &[ - ( - Some(MacAddress::parse_str("12-34-56-AB-CD-EF").unwrap()), - "'12-34-56-ab-cd-ef'", - ), - (None, "NULL"), - ], - ) - .await -} diff --git a/tokio-postgres/tests/test/types/mod.rs b/tokio-postgres/tests/test/types/mod.rs index f1a44da08..62d54372a 100644 --- a/tokio-postgres/tests/test/types/mod.rs +++ b/tokio-postgres/tests/test/types/mod.rs @@ -17,8 +17,6 @@ use bytes::BytesMut; mod bit_vec_06; #[cfg(feature = "with-chrono-0_4")] mod chrono_04; -#[cfg(feature = "with-eui48-0_4")] -mod eui48_04; #[cfg(feature = "with-eui48-1")] mod eui48_1; #[cfg(feature = "with-geo-types-0_6")] From ea9e0e5cddc2e772179027e635afa11d64feea2b Mon Sep 17 00:00:00 2001 From: ds-cbo <82801887+ds-cbo@users.noreply.github.com> Date: Mon, 30 Oct 2023 10:43:56 +0100 Subject: [PATCH 103/207] replace deprecated chrono::DateTime::from_utc --- postgres-types/src/chrono_04.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-types/src/chrono_04.rs b/postgres-types/src/chrono_04.rs index 0ec92437d..6011b549e 100644 --- a/postgres-types/src/chrono_04.rs +++ b/postgres-types/src/chrono_04.rs @@ -40,7 +40,7 @@ impl ToSql for NaiveDateTime { impl<'a> FromSql<'a> for DateTime { fn from_sql(type_: &Type, raw: &[u8]) -> Result, Box> { let naive = NaiveDateTime::from_sql(type_, raw)?; - Ok(DateTime::from_utc(naive, Utc)) + Ok(Utc::from_utc_datetime(naive)) } accepts!(TIMESTAMPTZ); From b4ebc4e7ec6ee52930bc22e2ad29b66687852623 Mon Sep 17 00:00:00 2001 From: ds-cbo <82801887+ds-cbo@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:39:50 +0100 Subject: [PATCH 104/207] add missing import --- postgres-types/src/chrono_04.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/postgres-types/src/chrono_04.rs b/postgres-types/src/chrono_04.rs index 6011b549e..f995d483c 100644 --- a/postgres-types/src/chrono_04.rs +++ b/postgres-types/src/chrono_04.rs @@ -1,5 +1,7 @@ use bytes::BytesMut; -use chrono_04::{DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; +use chrono_04::{ + DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc, +}; use postgres_protocol::types; use std::error::Error; From 19a6ef767bf6b2070ffe9efd43af514b6a31f2d2 Mon Sep 17 00:00:00 2001 From: ds-cbo <82801887+ds-cbo@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:54:13 +0100 Subject: [PATCH 105/207] fix more deprecated chrono functions --- postgres-types/src/chrono_04.rs | 2 +- tokio-postgres/tests/test/types/chrono_04.rs | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/postgres-types/src/chrono_04.rs b/postgres-types/src/chrono_04.rs index f995d483c..d599bde02 100644 --- a/postgres-types/src/chrono_04.rs +++ b/postgres-types/src/chrono_04.rs @@ -42,7 +42,7 @@ impl ToSql for NaiveDateTime { impl<'a> FromSql<'a> for DateTime { fn from_sql(type_: &Type, raw: &[u8]) -> Result, Box> { let naive = NaiveDateTime::from_sql(type_, raw)?; - Ok(Utc::from_utc_datetime(naive)) + Ok(Utc.from_utc_datetime(&naive)) } accepts!(TIMESTAMPTZ); diff --git a/tokio-postgres/tests/test/types/chrono_04.rs b/tokio-postgres/tests/test/types/chrono_04.rs index a8e9e5afa..b010055ba 100644 --- a/tokio-postgres/tests/test/types/chrono_04.rs +++ b/tokio-postgres/tests/test/types/chrono_04.rs @@ -53,10 +53,9 @@ async fn test_with_special_naive_date_time_params() { async fn test_date_time_params() { fn make_check(time: &str) -> (Option>, &str) { ( - Some( - Utc.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'") - .unwrap(), - ), + Some(Utc.from_utc_datetime( + &NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap(), + )), time, ) } @@ -76,10 +75,9 @@ async fn test_date_time_params() { async fn test_with_special_date_time_params() { fn make_check(time: &str) -> (Timestamp>, &str) { ( - Timestamp::Value( - Utc.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'") - .unwrap(), - ), + Timestamp::Value(Utc.from_utc_datetime( + &NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap(), + )), time, ) } From 863c1d6039e8fe114e48d62c0451d6eb5e4867a2 Mon Sep 17 00:00:00 2001 From: James Guthrie Date: Tue, 7 Nov 2023 22:09:39 +0100 Subject: [PATCH 106/207] fix code block --- postgres-types/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index 52b5c773a..aaf145e6b 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -168,6 +168,8 @@ //! 'Happy' //! ); //! ``` +//! +//! ```rust //! #[postgres(allow_mismatch)] //! enum Mood { //! Happy, From 10edbcb46c44933417e8d2e7a1c1d63c4119beb3 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 7 Nov 2023 16:23:06 -0500 Subject: [PATCH 107/207] Update lib.rs --- postgres-types/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index aaf145e6b..2f02f6e5f 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -170,6 +170,11 @@ //! ``` //! //! ```rust +//! # #[cfg(feature = "derive")] +//! use postgres_types::{ToSql, FromSql}; +//! +//! # #[cfg(feature = "derive")] +//! #[derive(Debug, ToSql, FromSql)] //! #[postgres(allow_mismatch)] //! enum Mood { //! Happy, From 02bab67280f8a850b816754b29eb0364708604ec Mon Sep 17 00:00:00 2001 From: "Michael P. Jung" Date: Tue, 5 Dec 2023 13:54:20 +0100 Subject: [PATCH 108/207] Add table_oid and field_id to columns of prepared statements --- tokio-postgres/CHANGELOG.md | 2 +- tokio-postgres/src/prepare.rs | 8 +++++++- tokio-postgres/src/statement.rs | 23 +++++++++++++++++------ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index bd076eef9..9f5eb9521 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -4,7 +4,7 @@ * Disable `rustc-serialize` compatibility of `eui48-1` dependency * Remove tests for `eui48-04` - +* Add `table_oid` and `field_id` fields to `Columns` struct of prepared statements. ## v0.7.10 - 2023-08-25 diff --git a/tokio-postgres/src/prepare.rs b/tokio-postgres/src/prepare.rs index e3f09a7c2..1ab34e2df 100644 --- a/tokio-postgres/src/prepare.rs +++ b/tokio-postgres/src/prepare.rs @@ -12,6 +12,7 @@ use log::debug; use postgres_protocol::message::backend::Message; use postgres_protocol::message::frontend; use std::future::Future; +use std::num::{NonZeroI16, NonZeroU32}; use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -95,7 +96,12 @@ pub async fn prepare( let mut it = row_description.fields(); while let Some(field) = it.next().map_err(Error::parse)? { let type_ = get_type(client, field.type_oid()).await?; - let column = Column::new(field.name().to_string(), type_); + let column = Column { + name: field.name().to_string(), + table_oid: NonZeroU32::new(field.table_oid()), + column_id: NonZeroI16::new(field.column_id()), + type_, + }; columns.push(column); } } diff --git a/tokio-postgres/src/statement.rs b/tokio-postgres/src/statement.rs index 97561a8e4..73d56c220 100644 --- a/tokio-postgres/src/statement.rs +++ b/tokio-postgres/src/statement.rs @@ -5,6 +5,7 @@ use crate::types::Type; use postgres_protocol::message::frontend; use std::{ fmt, + num::{NonZeroI16, NonZeroU32}, sync::{Arc, Weak}, }; @@ -66,20 +67,28 @@ impl Statement { /// Information about a column of a query. pub struct Column { - name: String, - type_: Type, + pub(crate) name: String, + pub(crate) table_oid: Option, + pub(crate) column_id: Option, + pub(crate) type_: Type, } impl Column { - pub(crate) fn new(name: String, type_: Type) -> Column { - Column { name, type_ } - } - /// Returns the name of the column. pub fn name(&self) -> &str { &self.name } + /// Returns the OID of the underlying database table. + pub fn table_oid(&self) -> Option { + self.table_oid + } + + /// Return the column ID within the underlying database table. + pub fn column_id(&self) -> Option { + self.column_id + } + /// Returns the type of the column. pub fn type_(&self) -> &Type { &self.type_ @@ -90,6 +99,8 @@ impl fmt::Debug for Column { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("Column") .field("name", &self.name) + .field("table_oid", &self.table_oid) + .field("column_id", &self.column_id) .field("type", &self.type_) .finish() } From 87876150d79e637767247176e339bf01a8b32d3b Mon Sep 17 00:00:00 2001 From: "Michael P. Jung" Date: Tue, 5 Dec 2023 14:09:44 +0100 Subject: [PATCH 109/207] Simplify Debug impl of Column --- tokio-postgres/src/prepare.rs | 2 +- tokio-postgres/src/statement.rs | 17 +++-------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/tokio-postgres/src/prepare.rs b/tokio-postgres/src/prepare.rs index 1ab34e2df..0302cdb4c 100644 --- a/tokio-postgres/src/prepare.rs +++ b/tokio-postgres/src/prepare.rs @@ -100,7 +100,7 @@ pub async fn prepare( name: field.name().to_string(), table_oid: NonZeroU32::new(field.table_oid()), column_id: NonZeroI16::new(field.column_id()), - type_, + r#type: type_, }; columns.push(column); } diff --git a/tokio-postgres/src/statement.rs b/tokio-postgres/src/statement.rs index 73d56c220..fe3b6b7a1 100644 --- a/tokio-postgres/src/statement.rs +++ b/tokio-postgres/src/statement.rs @@ -4,7 +4,6 @@ use crate::connection::RequestMessages; use crate::types::Type; use postgres_protocol::message::frontend; use std::{ - fmt, num::{NonZeroI16, NonZeroU32}, sync::{Arc, Weak}, }; @@ -66,11 +65,12 @@ impl Statement { } /// Information about a column of a query. +#[derive(Debug)] pub struct Column { pub(crate) name: String, pub(crate) table_oid: Option, pub(crate) column_id: Option, - pub(crate) type_: Type, + pub(crate) r#type: Type, } impl Column { @@ -91,17 +91,6 @@ impl Column { /// Returns the type of the column. pub fn type_(&self) -> &Type { - &self.type_ - } -} - -impl fmt::Debug for Column { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("Column") - .field("name", &self.name) - .field("table_oid", &self.table_oid) - .field("column_id", &self.column_id) - .field("type", &self.type_) - .finish() + &self.r#type } } From bbc04145de7a83dfa66cb3cf4a68878da2c1cc32 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 11 Dec 2023 19:06:22 -0500 Subject: [PATCH 110/207] Update id types --- tokio-postgres/src/prepare.rs | 5 ++--- tokio-postgres/src/statement.rs | 13 +++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/tokio-postgres/src/prepare.rs b/tokio-postgres/src/prepare.rs index 0302cdb4c..07fb45694 100644 --- a/tokio-postgres/src/prepare.rs +++ b/tokio-postgres/src/prepare.rs @@ -12,7 +12,6 @@ use log::debug; use postgres_protocol::message::backend::Message; use postgres_protocol::message::frontend; use std::future::Future; -use std::num::{NonZeroI16, NonZeroU32}; use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -98,8 +97,8 @@ pub async fn prepare( let type_ = get_type(client, field.type_oid()).await?; let column = Column { name: field.name().to_string(), - table_oid: NonZeroU32::new(field.table_oid()), - column_id: NonZeroI16::new(field.column_id()), + table_oid: Some(field.table_oid()).filter(|n| *n != 0), + column_id: Some(field.column_id()).filter(|n| *n != 0), r#type: type_, }; columns.push(column); diff --git a/tokio-postgres/src/statement.rs b/tokio-postgres/src/statement.rs index fe3b6b7a1..c5d657738 100644 --- a/tokio-postgres/src/statement.rs +++ b/tokio-postgres/src/statement.rs @@ -3,10 +3,7 @@ use crate::codec::FrontendMessage; use crate::connection::RequestMessages; use crate::types::Type; use postgres_protocol::message::frontend; -use std::{ - num::{NonZeroI16, NonZeroU32}, - sync::{Arc, Weak}, -}; +use std::sync::{Arc, Weak}; struct StatementInner { client: Weak, @@ -68,8 +65,8 @@ impl Statement { #[derive(Debug)] pub struct Column { pub(crate) name: String, - pub(crate) table_oid: Option, - pub(crate) column_id: Option, + pub(crate) table_oid: Option, + pub(crate) column_id: Option, pub(crate) r#type: Type, } @@ -80,12 +77,12 @@ impl Column { } /// Returns the OID of the underlying database table. - pub fn table_oid(&self) -> Option { + pub fn table_oid(&self) -> Option { self.table_oid } /// Return the column ID within the underlying database table. - pub fn column_id(&self) -> Option { + pub fn column_id(&self) -> Option { self.column_id } From 90c92c2ae8577a8e771333c701280485c45ad602 Mon Sep 17 00:00:00 2001 From: Troy Benson Date: Mon, 15 Jan 2024 16:33:27 +0000 Subject: [PATCH 111/207] feat(types): add default derive to json wrapper Adds a Default impl for `Json where T: Default` allowing for other structs to use the wrapper and implement Default. --- postgres-types/src/serde_json_1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-types/src/serde_json_1.rs b/postgres-types/src/serde_json_1.rs index b98d561d1..715c33f98 100644 --- a/postgres-types/src/serde_json_1.rs +++ b/postgres-types/src/serde_json_1.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; use std::io::Read; /// A wrapper type to allow arbitrary `Serialize`/`Deserialize` types to convert to Postgres JSON values. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct Json(pub T); impl<'a, T> FromSql<'a> for Json From 2f150a7e50ee03cbccf52792b7e4507dbcef0301 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:49:51 +0000 Subject: [PATCH 112/207] Update env_logger requirement from 0.10 to 0.11 Updates the requirements on [env_logger](https://github.com/rust-cli/env_logger) to permit the latest version. - [Release notes](https://github.com/rust-cli/env_logger/releases) - [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-cli/env_logger/compare/v0.10.0...v0.11.0) --- updated-dependencies: - dependency-name: env_logger dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- tokio-postgres/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index bb58eb2d9..237f3d2f1 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -67,7 +67,7 @@ socket2 = { version = "0.5", features = ["all"] } [dev-dependencies] futures-executor = "0.3" criterion = "0.5" -env_logger = "0.10" +env_logger = "0.11" tokio = { version = "1.0", features = [ "macros", "net", From 7bc3deb989b3030681b742801bfeaca7f67e1e1e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 22 Jan 2024 20:49:47 -0500 Subject: [PATCH 113/207] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 008158fb0..0cc823d35 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,7 @@ jobs: - run: docker compose up -d - uses: sfackler/actions/rustup@master with: - version: 1.70.0 + version: 1.71.0 - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - uses: actions/cache@v3 From a92c6eb2b65e12d7145a14cec23888d64c4b13e4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Mon, 22 Jan 2024 20:54:11 -0500 Subject: [PATCH 114/207] Update main.rs --- tokio-postgres/tests/test/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 0ab4a7bab..737f46631 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -303,6 +303,7 @@ async fn custom_range() { } #[tokio::test] +#[allow(clippy::get_first)] async fn simple_query() { let client = connect("user=postgres").await; From 289cf887600785e723628dcbc1f7a2267cd52917 Mon Sep 17 00:00:00 2001 From: Charles Samuels Date: Fri, 16 Feb 2024 10:55:08 -0800 Subject: [PATCH 115/207] add #[track_caller] to the Row::get() functions This small quality-of-life improvement changes these errors: thread '' panicked at /../.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-postgres-0.7.10/src/row.rs:151:25: error retrieving column 0: error deserializing column 0: a Postgres value was `NULL` to: thread '' panicked at my-program.rs:100:25: error retrieving column 0: error deserializing column 0: a Postgres value was `NULL` --- tokio-postgres/src/row.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tokio-postgres/src/row.rs b/tokio-postgres/src/row.rs index db179b432..3c79de603 100644 --- a/tokio-postgres/src/row.rs +++ b/tokio-postgres/src/row.rs @@ -141,6 +141,7 @@ impl Row { /// # Panics /// /// Panics if the index is out of bounds or if the value cannot be converted to the specified type. + #[track_caller] pub fn get<'a, I, T>(&'a self, idx: I) -> T where I: RowIndex + fmt::Display, @@ -239,6 +240,7 @@ impl SimpleQueryRow { /// # Panics /// /// Panics if the index is out of bounds or if the value cannot be converted to the specified type. + #[track_caller] pub fn get(&self, idx: I) -> Option<&str> where I: RowIndex + fmt::Display, From 25314a91c95dc8f75062e337eb363188c63df5d4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 17 Feb 2024 09:52:44 -0500 Subject: [PATCH 116/207] Bump CI version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cc823d35..641a42722 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,7 @@ jobs: - run: docker compose up -d - uses: sfackler/actions/rustup@master with: - version: 1.71.0 + version: 1.74.0 - run: echo "version=$(rustc --version)" >> $GITHUB_OUTPUT id: rust-version - uses: actions/cache@v3 From a9ca481c88fb619c6d35f2a6b64253bb46240c5d Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sun, 3 Mar 2024 16:37:30 +0100 Subject: [PATCH 117/207] Added ReadOnly session attr --- tokio-postgres/src/config.rs | 3 +++ tokio-postgres/tests/test/parse.rs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index b178eac80..c78346fff 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -34,6 +34,8 @@ pub enum TargetSessionAttrs { Any, /// The session must allow writes. ReadWrite, + /// The session allow only reads. + ReadOnly, } /// TLS configuration. @@ -622,6 +624,7 @@ impl Config { let target_session_attrs = match value { "any" => TargetSessionAttrs::Any, "read-write" => TargetSessionAttrs::ReadWrite, + "read-only" => TargetSessionAttrs::ReadOnly, _ => { return Err(Error::config_parse(Box::new(InvalidValue( "target_session_attrs", diff --git a/tokio-postgres/tests/test/parse.rs b/tokio-postgres/tests/test/parse.rs index 2c11899ca..04d422e27 100644 --- a/tokio-postgres/tests/test/parse.rs +++ b/tokio-postgres/tests/test/parse.rs @@ -34,6 +34,14 @@ fn settings() { .keepalives_idle(Duration::from_secs(30)) .target_session_attrs(TargetSessionAttrs::ReadWrite), ); + check( + "connect_timeout=3 keepalives=0 keepalives_idle=30 target_session_attrs=read-only", + Config::new() + .connect_timeout(Duration::from_secs(3)) + .keepalives(false) + .keepalives_idle(Duration::from_secs(30)) + .target_session_attrs(TargetSessionAttrs::ReadOnly), + ); } #[test] From 6a01730cbfed5d9c0aa694401704e6fe7ec0c8b5 Mon Sep 17 00:00:00 2001 From: "chandr-andr (Kiselev Aleksandr)" Date: Sun, 3 Mar 2024 19:17:50 +0100 Subject: [PATCH 118/207] Added ReadOnly session attr --- tokio-postgres/src/connect.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tokio-postgres/src/connect.rs b/tokio-postgres/src/connect.rs index ca57b9cdd..8189cb91c 100644 --- a/tokio-postgres/src/connect.rs +++ b/tokio-postgres/src/connect.rs @@ -160,7 +160,7 @@ where let has_hostname = hostname.is_some(); let (mut client, mut connection) = connect_raw(socket, tls, has_hostname, config).await?; - if let TargetSessionAttrs::ReadWrite = config.target_session_attrs { + if config.target_session_attrs != TargetSessionAttrs::Any { let rows = client.simple_query_raw("SHOW transaction_read_only"); pin_mut!(rows); @@ -185,11 +185,21 @@ where match next.await.transpose()? { Some(SimpleQueryMessage::Row(row)) => { - if row.try_get(0)? == Some("on") { + let read_only_result = row.try_get(0)?; + if read_only_result == Some("on") + && config.target_session_attrs == TargetSessionAttrs::ReadWrite + { return Err(Error::connect(io::Error::new( io::ErrorKind::PermissionDenied, "database does not allow writes", ))); + } else if read_only_result == Some("off") + && config.target_session_attrs == TargetSessionAttrs::ReadOnly + { + return Err(Error::connect(io::Error::new( + io::ErrorKind::PermissionDenied, + "database is not read only", + ))); } else { break; } From 4217553586c4ce390179a281834b8f2c3197863e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:45:26 +0000 Subject: [PATCH 119/207] Update base64 requirement from 0.21 to 0.22 Updates the requirements on [base64](https://github.com/marshallpierce/rust-base64) to permit the latest version. - [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md) - [Commits](https://github.com/marshallpierce/rust-base64/compare/v0.21.0...v0.22.0) --- updated-dependencies: - dependency-name: base64 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- postgres-protocol/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index b44994811..bc83fc4e6 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -13,7 +13,7 @@ default = [] js = ["getrandom/js"] [dependencies] -base64 = "0.21" +base64 = "0.22" byteorder = "1.0" bytes = "1.0" fallible-iterator = "0.2" From 9d7c43c73955638624e75957a333fac5d9be1c02 Mon Sep 17 00:00:00 2001 From: novacrazy Date: Sun, 11 Feb 2024 03:03:19 -0600 Subject: [PATCH 120/207] Shrink query_opt/query_one codegen size very slightly --- tokio-postgres/src/client.rs | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index 427a05049..d48a23a60 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -274,19 +274,9 @@ impl Client { where T: ?Sized + ToStatement, { - let stream = self.query_raw(statement, slice_iter(params)).await?; - pin_mut!(stream); - - let row = match stream.try_next().await? { - Some(row) => row, - None => return Err(Error::row_count()), - }; - - if stream.try_next().await?.is_some() { - return Err(Error::row_count()); - } - - Ok(row) + self.query_opt(statement, params) + .await + .and_then(|res| res.ok_or_else(Error::row_count)) } /// Executes a statements which returns zero or one rows, returning it. @@ -310,16 +300,22 @@ impl Client { let stream = self.query_raw(statement, slice_iter(params)).await?; pin_mut!(stream); - let row = match stream.try_next().await? { - Some(row) => row, - None => return Ok(None), - }; + let mut first = None; + + // Originally this was two calls to `try_next().await?`, + // once for the first element, and second to error if more than one. + // + // However, this new form with only one .await in a loop generates + // slightly smaller codegen/stack usage for the resulting future. + while let Some(row) = stream.try_next().await? { + if first.is_some() { + return Err(Error::row_count()); + } - if stream.try_next().await?.is_some() { - return Err(Error::row_count()); + first = Some(row); } - Ok(Some(row)) + Ok(first) } /// The maximally flexible version of [`query`]. From 97436303232127dbd448d71a50c6365bdbee083c Mon Sep 17 00:00:00 2001 From: laxjesse Date: Wed, 13 Mar 2024 11:10:58 -0400 Subject: [PATCH 121/207] use `split_once` instead of `split` to parse lsn strings [`str::split`](https://doc.rust-lang.org/std/primitive.str.html#method.split) allocates a vector and generates considerably more instructions when compiled than [`str::split_once`](https://doc.rust-lang.org/std/primitive.str.html#method.split_once). [`u64::from_str_radix(split_lo, 16)`](https://doc.rust-lang.org/std/primitive.u64.html#method.from_str_radix) will error if the `lsn_str` contains more than one `/` so this change should result in the same behavior as the current implementation despite not explicitly checking this. --- postgres-types/CHANGELOG.md | 6 ++++++ postgres-types/src/pg_lsn.rs | 18 ++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/postgres-types/CHANGELOG.md b/postgres-types/CHANGELOG.md index 72a1cbb6a..157a2cc7d 100644 --- a/postgres-types/CHANGELOG.md +++ b/postgres-types/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## Unreleased + +### Changed + +* `FromStr` implementation for `PgLsn` no longer allocates a `Vec` when splitting an lsn string on it's `/`. + ## v0.2.6 - 2023-08-19 ### Fixed diff --git a/postgres-types/src/pg_lsn.rs b/postgres-types/src/pg_lsn.rs index f0bbf4022..f339f9689 100644 --- a/postgres-types/src/pg_lsn.rs +++ b/postgres-types/src/pg_lsn.rs @@ -33,16 +33,14 @@ impl FromStr for PgLsn { type Err = ParseLsnError; fn from_str(lsn_str: &str) -> Result { - let split: Vec<&str> = lsn_str.split('/').collect(); - if split.len() == 2 { - let (hi, lo) = ( - u64::from_str_radix(split[0], 16).map_err(|_| ParseLsnError(()))?, - u64::from_str_radix(split[1], 16).map_err(|_| ParseLsnError(()))?, - ); - Ok(PgLsn((hi << 32) | lo)) - } else { - Err(ParseLsnError(())) - } + let Some((split_hi, split_lo)) = lsn_str.split_once('/') else { + return Err(ParseLsnError(())); + }; + let (hi, lo) = ( + u64::from_str_radix(split_hi, 16).map_err(|_| ParseLsnError(()))?, + u64::from_str_radix(split_lo, 16).map_err(|_| ParseLsnError(()))?, + ); + Ok(PgLsn((hi << 32) | lo)) } } From 3836a3052065bccf53001b832a21823204bfa137 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Wed, 10 Apr 2024 17:42:13 +0200 Subject: [PATCH 122/207] Make license metadata SPDX compliant --- postgres-derive/Cargo.toml | 4 ++-- postgres-native-tls/Cargo.toml | 2 +- postgres-openssl/Cargo.toml | 2 +- postgres-protocol/Cargo.toml | 2 +- postgres-types/Cargo.toml | 2 +- postgres/Cargo.toml | 2 +- tokio-postgres/Cargo.toml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/postgres-derive/Cargo.toml b/postgres-derive/Cargo.toml index 51ebb5663..5d1604b24 100644 --- a/postgres-derive/Cargo.toml +++ b/postgres-derive/Cargo.toml @@ -2,7 +2,7 @@ name = "postgres-derive" version = "0.4.5" authors = ["Steven Fackler "] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" edition = "2018" description = "An internal crate used by postgres-types" repository = "https://github.com/sfackler/rust-postgres" @@ -15,4 +15,4 @@ test = false syn = "2.0" proc-macro2 = "1.0" quote = "1.0" -heck = "0.4" \ No newline at end of file +heck = "0.4" diff --git a/postgres-native-tls/Cargo.toml b/postgres-native-tls/Cargo.toml index 1f2f6385d..936eeeaa4 100644 --- a/postgres-native-tls/Cargo.toml +++ b/postgres-native-tls/Cargo.toml @@ -3,7 +3,7 @@ name = "postgres-native-tls" version = "0.5.0" authors = ["Steven Fackler "] edition = "2018" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" description = "TLS support for tokio-postgres via native-tls" repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" diff --git a/postgres-openssl/Cargo.toml b/postgres-openssl/Cargo.toml index 8671308af..b7ebd3385 100644 --- a/postgres-openssl/Cargo.toml +++ b/postgres-openssl/Cargo.toml @@ -3,7 +3,7 @@ name = "postgres-openssl" version = "0.5.0" authors = ["Steven Fackler "] edition = "2018" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" description = "TLS support for tokio-postgres via openssl" repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index bc83fc4e6..a8a130495 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -4,7 +4,7 @@ version = "0.6.6" authors = ["Steven Fackler "] edition = "2018" description = "Low level Postgres protocol APIs" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index cfd083637..bf011251b 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -3,7 +3,7 @@ name = "postgres-types" version = "0.2.6" authors = ["Steven Fackler "] edition = "2018" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" description = "Conversions between Rust and Postgres values" repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index 18406da9f..2ff3c875e 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -3,7 +3,7 @@ name = "postgres" version = "0.19.7" authors = ["Steven Fackler "] edition = "2018" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" description = "A native, synchronous PostgreSQL client" repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 237f3d2f1..b3e56314f 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -3,7 +3,7 @@ name = "tokio-postgres" version = "0.7.10" authors = ["Steven Fackler "] edition = "2018" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" description = "A native, asynchronous PostgreSQL client" repository = "https://github.com/sfackler/rust-postgres" readme = "../README.md" From 670cd7d5802dfb3b0b6b1eadd480f5c9730bb0b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:21:21 +0000 Subject: [PATCH 123/207] Update heck requirement from 0.4 to 0.5 --- updated-dependencies: - dependency-name: heck dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- postgres-derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-derive/Cargo.toml b/postgres-derive/Cargo.toml index 5d1604b24..cbae6c77b 100644 --- a/postgres-derive/Cargo.toml +++ b/postgres-derive/Cargo.toml @@ -15,4 +15,4 @@ test = false syn = "2.0" proc-macro2 = "1.0" quote = "1.0" -heck = "0.4" +heck = "0.5" From 3c6dbe9b8c7bfad82c646f34092e3fa1d321b723 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 1 May 2024 22:46:06 -0400 Subject: [PATCH 124/207] Avoid extra clone in config if possible Using `impl Into` instead of `&str` in a fn arg allows both `&str` and `String` as parameters - thus if the caller already has a String object that it doesn't need, it can pass it in without extra cloning. The same might be done with the password, but may require closer look. --- tokio-postgres/src/config.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tokio-postgres/src/config.rs b/tokio-postgres/src/config.rs index c78346fff..62b45f793 100644 --- a/tokio-postgres/src/config.rs +++ b/tokio-postgres/src/config.rs @@ -248,8 +248,8 @@ impl Config { /// Sets the user to authenticate with. /// /// Defaults to the user executing this process. - pub fn user(&mut self, user: &str) -> &mut Config { - self.user = Some(user.to_string()); + pub fn user(&mut self, user: impl Into) -> &mut Config { + self.user = Some(user.into()); self } @@ -277,8 +277,8 @@ impl Config { /// Sets the name of the database to connect to. /// /// Defaults to the user. - pub fn dbname(&mut self, dbname: &str) -> &mut Config { - self.dbname = Some(dbname.to_string()); + pub fn dbname(&mut self, dbname: impl Into) -> &mut Config { + self.dbname = Some(dbname.into()); self } @@ -289,8 +289,8 @@ impl Config { } /// Sets command line options used to configure the server. - pub fn options(&mut self, options: &str) -> &mut Config { - self.options = Some(options.to_string()); + pub fn options(&mut self, options: impl Into) -> &mut Config { + self.options = Some(options.into()); self } @@ -301,8 +301,8 @@ impl Config { } /// Sets the value of the `application_name` runtime parameter. - pub fn application_name(&mut self, application_name: &str) -> &mut Config { - self.application_name = Some(application_name.to_string()); + pub fn application_name(&mut self, application_name: impl Into) -> &mut Config { + self.application_name = Some(application_name.into()); self } @@ -330,7 +330,9 @@ impl Config { /// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix /// systems, a host starting with a `/` is interpreted as a path to a directory containing Unix domain sockets. /// There must be either no hosts, or the same number of hosts as hostaddrs. - pub fn host(&mut self, host: &str) -> &mut Config { + pub fn host(&mut self, host: impl Into) -> &mut Config { + let host = host.into(); + #[cfg(unix)] { if host.starts_with('/') { @@ -338,7 +340,7 @@ impl Config { } } - self.host.push(Host::Tcp(host.to_string())); + self.host.push(Host::Tcp(host)); self } @@ -990,7 +992,7 @@ impl<'a> UrlParser<'a> { let mut it = creds.splitn(2, ':'); let user = self.decode(it.next().unwrap())?; - self.config.user(&user); + self.config.user(user); if let Some(password) = it.next() { let password = Cow::from(percent_encoding::percent_decode(password.as_bytes())); @@ -1053,7 +1055,7 @@ impl<'a> UrlParser<'a> { }; if !dbname.is_empty() { - self.config.dbname(&self.decode(dbname)?); + self.config.dbname(self.decode(dbname)?); } Ok(()) From d5d75d3a2f064425436c08b6a8f2da2b985aab3d Mon Sep 17 00:00:00 2001 From: vsuryamurthy Date: Thu, 23 May 2024 17:18:41 +0200 Subject: [PATCH 125/207] add simple_query to GenericClient in tokio_postgres --- tokio-postgres/CHANGELOG.md | 1 + tokio-postgres/src/generic_client.rs | 35 ++++++++++++++++++---------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index 9f5eb9521..775c22e34 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -5,6 +5,7 @@ * Disable `rustc-serialize` compatibility of `eui48-1` dependency * Remove tests for `eui48-04` * Add `table_oid` and `field_id` fields to `Columns` struct of prepared statements. +* Add `GenericClient::simple_query`. ## v0.7.10 - 2023-08-25 diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index 50cff9712..d80dd3b86 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -1,6 +1,6 @@ use crate::query::RowStream; use crate::types::{BorrowToSql, ToSql, Type}; -use crate::{Client, Error, Row, Statement, ToStatement, Transaction}; +use crate::{Client, Error, Row, SimpleQueryMessage, Statement, ToStatement, Transaction}; use async_trait::async_trait; mod private { @@ -12,12 +12,12 @@ mod private { /// This trait is "sealed", and cannot be implemented outside of this crate. #[async_trait] pub trait GenericClient: private::Sealed { - /// Like `Client::execute`. + /// Like [`Client::execute`]. async fn execute(&self, query: &T, params: &[&(dyn ToSql + Sync)]) -> Result where T: ?Sized + ToStatement + Sync + Send; - /// Like `Client::execute_raw`. + /// Like [`Client::execute_raw`]. async fn execute_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement + Sync + Send, @@ -25,12 +25,12 @@ pub trait GenericClient: private::Sealed { I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator; - /// Like `Client::query`. + /// Like [`Client::query`]. async fn query(&self, query: &T, params: &[&(dyn ToSql + Sync)]) -> Result, Error> where T: ?Sized + ToStatement + Sync + Send; - /// Like `Client::query_one`. + /// Like [`Client::query_one`]. async fn query_one( &self, statement: &T, @@ -39,7 +39,7 @@ pub trait GenericClient: private::Sealed { where T: ?Sized + ToStatement + Sync + Send; - /// Like `Client::query_opt`. + /// Like [`Client::query_opt`]. async fn query_opt( &self, statement: &T, @@ -48,7 +48,7 @@ pub trait GenericClient: private::Sealed { where T: ?Sized + ToStatement + Sync + Send; - /// Like `Client::query_raw`. + /// Like [`Client::query_raw`]. async fn query_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement + Sync + Send, @@ -56,23 +56,26 @@ pub trait GenericClient: private::Sealed { I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator; - /// Like `Client::prepare`. + /// Like [`Client::prepare`]. async fn prepare(&self, query: &str) -> Result; - /// Like `Client::prepare_typed`. + /// Like [`Client::prepare_typed`]. async fn prepare_typed( &self, query: &str, parameter_types: &[Type], ) -> Result; - /// Like `Client::transaction`. + /// Like [`Client::transaction`]. async fn transaction(&mut self) -> Result, Error>; - /// Like `Client::batch_execute`. + /// Like [`Client::batch_execute`]. async fn batch_execute(&self, query: &str) -> Result<(), Error>; - /// Returns a reference to the underlying `Client`. + /// Like [`Client::simple_query`]. + async fn simple_query(&self, query: &str) -> Result, Error>; + + /// Returns a reference to the underlying [`Client`]. fn client(&self) -> &Client; } @@ -156,6 +159,10 @@ impl GenericClient for Client { self.batch_execute(query).await } + async fn simple_query(&self, query: &str) -> Result, Error> { + self.simple_query(query).await + } + fn client(&self) -> &Client { self } @@ -243,6 +250,10 @@ impl GenericClient for Transaction<'_> { self.batch_execute(query).await } + async fn simple_query(&self, query: &str) -> Result, Error> { + self.simple_query(query).await + } + fn client(&self) -> &Client { self.client() } From fbecae11ace79376b20ae8b9a587ab577e8287cd Mon Sep 17 00:00:00 2001 From: Duarte Nunes Date: Mon, 11 Mar 2024 14:43:50 -0300 Subject: [PATCH 126/207] feat(types): add 'js' feature for wasm Enables the "js" feature of postgres-protocol. --- postgres-types/Cargo.toml | 1 + tokio-postgres/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index bf011251b..33296db2c 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -13,6 +13,7 @@ categories = ["database"] [features] derive = ["postgres-derive"] array-impls = ["array-init"] +js = ["postgres-protocol/js"] with-bit-vec-0_6 = ["bit-vec-06"] with-cidr-0_2 = ["cidr-02"] with-chrono-0_4 = ["chrono-04"] diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index b3e56314f..2e080cfb2 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -40,7 +40,7 @@ with-uuid-0_8 = ["postgres-types/with-uuid-0_8"] with-uuid-1 = ["postgres-types/with-uuid-1"] with-time-0_2 = ["postgres-types/with-time-0_2"] with-time-0_3 = ["postgres-types/with-time-0_3"] -js = ["postgres-protocol/js"] +js = ["postgres-protocol/js", "postgres-types/js"] [dependencies] async-trait = "0.1" From 6cd4652bad6ac8474235c23d0e4e96cc4aa4d8db Mon Sep 17 00:00:00 2001 From: Dane Rigby Date: Tue, 28 May 2024 21:57:27 -0500 Subject: [PATCH 127/207] Add RowDescription to SimpleQueryMessage --- tokio-postgres/src/lib.rs | 6 ++++++ tokio-postgres/src/simple_query.rs | 5 +++-- tokio-postgres/tests/test/main.rs | 13 ++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs index 2973d33b0..d650f4db9 100644 --- a/tokio-postgres/src/lib.rs +++ b/tokio-postgres/src/lib.rs @@ -118,6 +118,10 @@ //! | `with-time-0_3` | Enable support for the 0.3 version of the `time` crate. | [time](https://crates.io/crates/time/0.3.0) 0.3 | no | #![warn(rust_2018_idioms, clippy::all, missing_docs)] +use std::sync::Arc; + +use simple_query::SimpleColumn; + pub use crate::cancel_token::CancelToken; pub use crate::client::Client; pub use crate::config::Config; @@ -248,6 +252,8 @@ pub enum SimpleQueryMessage { /// /// The number of rows modified or selected is returned. CommandComplete(u64), + /// Column values of the proceeding row values + RowDescription(Arc<[SimpleColumn]>) } fn slice_iter<'a>( diff --git a/tokio-postgres/src/simple_query.rs b/tokio-postgres/src/simple_query.rs index bcc6d928b..4e0b7734d 100644 --- a/tokio-postgres/src/simple_query.rs +++ b/tokio-postgres/src/simple_query.rs @@ -95,14 +95,15 @@ impl Stream for SimpleQueryStream { return Poll::Ready(Some(Ok(SimpleQueryMessage::CommandComplete(0)))); } Message::RowDescription(body) => { - let columns = body + let columns: Arc<[SimpleColumn]> = body .fields() .map(|f| Ok(SimpleColumn::new(f.name().to_string()))) .collect::>() .map_err(Error::parse)? .into(); - *this.columns = Some(columns); + *this.columns = Some(columns.clone()); + return Poll::Ready(Some(Ok(SimpleQueryMessage::RowDescription(columns.clone())))); } Message::DataRow(body) => { let row = match &this.columns { diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 737f46631..4fa72aec9 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -328,6 +328,13 @@ async fn simple_query() { _ => panic!("unexpected message"), } match &messages[2] { + SimpleQueryMessage::RowDescription(columns) => { + assert_eq!(columns.get(0).map(|c| c.name()), Some("id")); + assert_eq!(columns.get(1).map(|c| c.name()), Some("name")); + } + _ => panic!("unexpected message") + } + match &messages[3] { SimpleQueryMessage::Row(row) => { assert_eq!(row.columns().get(0).map(|c| c.name()), Some("id")); assert_eq!(row.columns().get(1).map(|c| c.name()), Some("name")); @@ -336,7 +343,7 @@ async fn simple_query() { } _ => panic!("unexpected message"), } - match &messages[3] { + match &messages[4] { SimpleQueryMessage::Row(row) => { assert_eq!(row.columns().get(0).map(|c| c.name()), Some("id")); assert_eq!(row.columns().get(1).map(|c| c.name()), Some("name")); @@ -345,11 +352,11 @@ async fn simple_query() { } _ => panic!("unexpected message"), } - match messages[4] { + match messages[5] { SimpleQueryMessage::CommandComplete(2) => {} _ => panic!("unexpected message"), } - assert_eq!(messages.len(), 5); + assert_eq!(messages.len(), 6); } #[tokio::test] From 7afead9a13d54f1c5ce9bef5eda1fb7ced26db61 Mon Sep 17 00:00:00 2001 From: Dane Rigby Date: Tue, 28 May 2024 22:08:41 -0500 Subject: [PATCH 128/207] Formatting updates --- tokio-postgres/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs index d650f4db9..6c6266736 100644 --- a/tokio-postgres/src/lib.rs +++ b/tokio-postgres/src/lib.rs @@ -118,10 +118,6 @@ //! | `with-time-0_3` | Enable support for the 0.3 version of the `time` crate. | [time](https://crates.io/crates/time/0.3.0) 0.3 | no | #![warn(rust_2018_idioms, clippy::all, missing_docs)] -use std::sync::Arc; - -use simple_query::SimpleColumn; - pub use crate::cancel_token::CancelToken; pub use crate::client::Client; pub use crate::config::Config; @@ -134,7 +130,7 @@ pub use crate::generic_client::GenericClient; pub use crate::portal::Portal; pub use crate::query::RowStream; pub use crate::row::{Row, SimpleQueryRow}; -pub use crate::simple_query::SimpleQueryStream; +pub use crate::simple_query::{SimpleQueryStream, SimpleColumn}; #[cfg(feature = "runtime")] pub use crate::socket::Socket; pub use crate::statement::{Column, Statement}; @@ -145,6 +141,7 @@ pub use crate::to_statement::ToStatement; pub use crate::transaction::Transaction; pub use crate::transaction_builder::{IsolationLevel, TransactionBuilder}; use crate::types::ToSql; +use std::sync::Arc; pub mod binary_copy; mod bind; From eec06021d9ebe1c1c2fcc47666a76ce257ae2891 Mon Sep 17 00:00:00 2001 From: Dane Rigby Date: Tue, 28 May 2024 23:50:50 -0500 Subject: [PATCH 129/207] Clippy compliance --- tokio-postgres/src/simple_query.rs | 54 ++++++++++++++---------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/tokio-postgres/src/simple_query.rs b/tokio-postgres/src/simple_query.rs index 4e0b7734d..e84806d36 100644 --- a/tokio-postgres/src/simple_query.rs +++ b/tokio-postgres/src/simple_query.rs @@ -85,36 +85,34 @@ impl Stream for SimpleQueryStream { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); - loop { - match ready!(this.responses.poll_next(cx)?) { - Message::CommandComplete(body) => { - let rows = extract_row_affected(&body)?; - return Poll::Ready(Some(Ok(SimpleQueryMessage::CommandComplete(rows)))); - } - Message::EmptyQueryResponse => { - return Poll::Ready(Some(Ok(SimpleQueryMessage::CommandComplete(0)))); - } - Message::RowDescription(body) => { - let columns: Arc<[SimpleColumn]> = body - .fields() - .map(|f| Ok(SimpleColumn::new(f.name().to_string()))) - .collect::>() - .map_err(Error::parse)? - .into(); + match ready!(this.responses.poll_next(cx)?) { + Message::CommandComplete(body) => { + let rows = extract_row_affected(&body)?; + Poll::Ready(Some(Ok(SimpleQueryMessage::CommandComplete(rows)))) + } + Message::EmptyQueryResponse => { + Poll::Ready(Some(Ok(SimpleQueryMessage::CommandComplete(0)))) + } + Message::RowDescription(body) => { + let columns: Arc<[SimpleColumn]> = body + .fields() + .map(|f| Ok(SimpleColumn::new(f.name().to_string()))) + .collect::>() + .map_err(Error::parse)? + .into(); - *this.columns = Some(columns.clone()); - return Poll::Ready(Some(Ok(SimpleQueryMessage::RowDescription(columns.clone())))); - } - Message::DataRow(body) => { - let row = match &this.columns { - Some(columns) => SimpleQueryRow::new(columns.clone(), body)?, - None => return Poll::Ready(Some(Err(Error::unexpected_message()))), - }; - return Poll::Ready(Some(Ok(SimpleQueryMessage::Row(row)))); - } - Message::ReadyForQuery(_) => return Poll::Ready(None), - _ => return Poll::Ready(Some(Err(Error::unexpected_message()))), + *this.columns = Some(columns.clone()); + Poll::Ready(Some(Ok(SimpleQueryMessage::RowDescription(columns.clone())))) + } + Message::DataRow(body) => { + let row = match &this.columns { + Some(columns) => SimpleQueryRow::new(columns.clone(), body)?, + None => return Poll::Ready(Some(Err(Error::unexpected_message()))), + }; + Poll::Ready(Some(Ok(SimpleQueryMessage::Row(row)))) } + Message::ReadyForQuery(_) => Poll::Ready(None), + _ => Poll::Ready(Some(Err(Error::unexpected_message()))), } } } From bd6350c2fff2201d680a1814acf7a9208f4b7ad4 Mon Sep 17 00:00:00 2001 From: Dane Rigby Date: Wed, 29 May 2024 23:32:18 -0500 Subject: [PATCH 130/207] Formatting --- tokio-postgres/src/lib.rs | 4 ++-- tokio-postgres/src/simple_query.rs | 4 +++- tokio-postgres/tests/test/main.rs | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs index 6c6266736..a603158fb 100644 --- a/tokio-postgres/src/lib.rs +++ b/tokio-postgres/src/lib.rs @@ -130,7 +130,7 @@ pub use crate::generic_client::GenericClient; pub use crate::portal::Portal; pub use crate::query::RowStream; pub use crate::row::{Row, SimpleQueryRow}; -pub use crate::simple_query::{SimpleQueryStream, SimpleColumn}; +pub use crate::simple_query::{SimpleColumn, SimpleQueryStream}; #[cfg(feature = "runtime")] pub use crate::socket::Socket; pub use crate::statement::{Column, Statement}; @@ -250,7 +250,7 @@ pub enum SimpleQueryMessage { /// The number of rows modified or selected is returned. CommandComplete(u64), /// Column values of the proceeding row values - RowDescription(Arc<[SimpleColumn]>) + RowDescription(Arc<[SimpleColumn]>), } fn slice_iter<'a>( diff --git a/tokio-postgres/src/simple_query.rs b/tokio-postgres/src/simple_query.rs index e84806d36..86af8e739 100644 --- a/tokio-postgres/src/simple_query.rs +++ b/tokio-postgres/src/simple_query.rs @@ -102,7 +102,9 @@ impl Stream for SimpleQueryStream { .into(); *this.columns = Some(columns.clone()); - Poll::Ready(Some(Ok(SimpleQueryMessage::RowDescription(columns.clone())))) + Poll::Ready(Some(Ok(SimpleQueryMessage::RowDescription( + columns.clone(), + )))) } Message::DataRow(body) => { let row = match &this.columns { diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 4fa72aec9..e85960ab6 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -330,9 +330,9 @@ async fn simple_query() { match &messages[2] { SimpleQueryMessage::RowDescription(columns) => { assert_eq!(columns.get(0).map(|c| c.name()), Some("id")); - assert_eq!(columns.get(1).map(|c| c.name()), Some("name")); + assert_eq!(columns.get(1).map(|c| c.name()), Some("name")); } - _ => panic!("unexpected message") + _ => panic!("unexpected message"), } match &messages[3] { SimpleQueryMessage::Row(row) => { From f3976680c6d7004b04b3ba39f90f2956ce6d7010 Mon Sep 17 00:00:00 2001 From: Ramnivas Laddad Date: Sun, 26 May 2024 11:05:00 -0700 Subject: [PATCH 131/207] Work with pools that don't support prepared statements Introduce a new `query_with_param_types` method that allows to specify Postgres type parameters. This obviated the need to use prepared statementsjust to obtain parameter types for a query. It then combines parse, bind, and execute in a single packet. Related: #1017, #1067 --- tokio-postgres/src/client.rs | 82 +++++++++++++++ tokio-postgres/src/generic_client.rs | 46 +++++++++ tokio-postgres/src/prepare.rs | 2 +- tokio-postgres/src/query.rs | 146 ++++++++++++++++++++++++++- tokio-postgres/src/statement.rs | 13 +++ tokio-postgres/src/transaction.rs | 27 +++++ tokio-postgres/tests/test/main.rs | 106 +++++++++++++++++++ 7 files changed, 416 insertions(+), 6 deletions(-) diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index d48a23a60..431bfa792 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -364,6 +364,88 @@ impl Client { query::query(&self.inner, statement, params).await } + /// Like `query`, but requires the types of query parameters to be explicitly specified. + /// + /// Compared to `query`, this method allows performing queries without three round trips (for prepare, execute, and close). Thus, + /// this is suitable in environments where prepared statements aren't supported (such as Cloudflare Workers with Hyperdrive). + /// + /// # Examples + /// + /// ```no_run + /// # async fn async_main(client: &tokio_postgres::Client) -> Result<(), tokio_postgres::Error> { + /// use tokio_postgres::types::ToSql; + /// use tokio_postgres::types::Type; + /// use futures_util::{pin_mut, TryStreamExt}; + /// + /// let rows = client.query_with_param_types( + /// "SELECT foo FROM bar WHERE biz = $1 AND baz = $2", + /// &[(&"first param", Type::TEXT), (&2i32, Type::INT4)], + /// ).await?; + /// + /// for row in rows { + /// let foo: i32 = row.get("foo"); + /// println!("foo: {}", foo); + /// } + /// # Ok(()) + /// # } + /// ``` + pub async fn query_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.query_raw_with_param_types(statement, params) + .await? + .try_collect() + .await + } + + /// The maximally flexible version of [`query_with_param_types`]. + /// + /// A statement may contain parameters, specified by `$n`, where `n` is the index of the parameter of the list + /// provided, 1-indexed. + /// + /// The parameters must specify value along with their Postgres type. This allows performing + /// queries without three round trips (for prepare, execute, and close). + /// + /// [`query_with_param_types`]: #method.query_with_param_types + /// + /// # Examples + /// + /// ```no_run + /// # async fn async_main(client: &tokio_postgres::Client) -> Result<(), tokio_postgres::Error> { + /// use tokio_postgres::types::ToSql; + /// use tokio_postgres::types::Type; + /// use futures_util::{pin_mut, TryStreamExt}; + /// + /// let mut it = client.query_raw_with_param_types( + /// "SELECT foo FROM bar WHERE biz = $1 AND baz = $2", + /// &[(&"first param", Type::TEXT), (&2i32, Type::INT4)], + /// ).await?; + /// + /// pin_mut!(it); + /// while let Some(row) = it.try_next().await? { + /// let foo: i32 = row.get("foo"); + /// println!("foo: {}", foo); + /// } + /// # Ok(()) + /// # } + /// ``` + pub async fn query_raw_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result { + fn slice_iter<'a>( + s: &'a [(&'a (dyn ToSql + Sync), Type)], + ) -> impl ExactSizeIterator + 'a { + s.iter() + .map(|(param, param_type)| (*param as _, param_type.clone())) + } + + query::query_with_param_types(&self.inner, statement, slice_iter(params)).await + } + /// Executes a statement, returning the number of rows modified. /// /// A statement may contain parameters, specified by `$n`, where `n` is the index of the parameter of the list diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index 50cff9712..3a0b09233 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -56,6 +56,20 @@ pub trait GenericClient: private::Sealed { I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator; + /// Like `Client::query_with_param_types` + async fn query_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error>; + + /// Like `Client::query_raw_with_param_types`. + async fn query_raw_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result; + /// Like `Client::prepare`. async fn prepare(&self, query: &str) -> Result; @@ -136,6 +150,22 @@ impl GenericClient for Client { self.query_raw(statement, params).await } + async fn query_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.query_with_param_types(statement, params).await + } + + async fn query_raw_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result { + self.query_raw_with_param_types(statement, params).await + } + async fn prepare(&self, query: &str) -> Result { self.prepare(query).await } @@ -222,6 +252,22 @@ impl GenericClient for Transaction<'_> { self.query_raw(statement, params).await } + async fn query_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.query_with_param_types(statement, params).await + } + + async fn query_raw_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result { + self.query_raw_with_param_types(statement, params).await + } + async fn prepare(&self, query: &str) -> Result { self.prepare(query).await } diff --git a/tokio-postgres/src/prepare.rs b/tokio-postgres/src/prepare.rs index 07fb45694..1d9bacb16 100644 --- a/tokio-postgres/src/prepare.rs +++ b/tokio-postgres/src/prepare.rs @@ -131,7 +131,7 @@ fn encode(client: &InnerClient, name: &str, query: &str, types: &[Type]) -> Resu }) } -async fn get_type(client: &Arc, oid: Oid) -> Result { +pub(crate) async fn get_type(client: &Arc, oid: Oid) -> Result { if let Some(type_) = Type::from_oid(oid) { return Ok(type_); } diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index e6e1d00a8..b9cc66405 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -1,17 +1,21 @@ use crate::client::{InnerClient, Responses}; use crate::codec::FrontendMessage; use crate::connection::RequestMessages; +use crate::prepare::get_type; use crate::types::{BorrowToSql, IsNull}; -use crate::{Error, Portal, Row, Statement}; +use crate::{Column, Error, Portal, Row, Statement}; use bytes::{Bytes, BytesMut}; +use fallible_iterator::FallibleIterator; use futures_util::{ready, Stream}; use log::{debug, log_enabled, Level}; use pin_project_lite::pin_project; -use postgres_protocol::message::backend::{CommandCompleteBody, Message}; +use postgres_protocol::message::backend::{CommandCompleteBody, Message, RowDescriptionBody}; use postgres_protocol::message::frontend; +use postgres_types::Type; use std::fmt; use std::marker::PhantomPinned; use std::pin::Pin; +use std::sync::Arc; use std::task::{Context, Poll}; struct BorrowToSqlParamsDebug<'a, T>(&'a [T]); @@ -50,13 +54,125 @@ where }; let responses = start(client, buf).await?; Ok(RowStream { - statement, + statement: statement, responses, rows_affected: None, _p: PhantomPinned, }) } +enum QueryProcessingState { + Empty, + ParseCompleted, + BindCompleted, + ParameterDescribed, + Final(Vec), +} + +/// State machine for processing messages for `query_with_param_types`. +impl QueryProcessingState { + pub async fn process_message( + self, + client: &Arc, + message: Message, + ) -> Result { + match (self, message) { + (QueryProcessingState::Empty, Message::ParseComplete) => { + Ok(QueryProcessingState::ParseCompleted) + } + (QueryProcessingState::ParseCompleted, Message::BindComplete) => { + Ok(QueryProcessingState::BindCompleted) + } + (QueryProcessingState::BindCompleted, Message::ParameterDescription(_)) => { + Ok(QueryProcessingState::ParameterDescribed) + } + ( + QueryProcessingState::ParameterDescribed, + Message::RowDescription(row_description), + ) => Self::form_final(client, Some(row_description)).await, + (QueryProcessingState::ParameterDescribed, Message::NoData) => { + Self::form_final(client, None).await + } + (_, Message::ErrorResponse(body)) => Err(Error::db(body)), + _ => Err(Error::unexpected_message()), + } + } + + async fn form_final( + client: &Arc, + row_description: Option, + ) -> Result { + let mut columns = vec![]; + if let Some(row_description) = row_description { + let mut it = row_description.fields(); + while let Some(field) = it.next().map_err(Error::parse)? { + let type_ = get_type(client, field.type_oid()).await?; + let column = Column { + name: field.name().to_string(), + table_oid: Some(field.table_oid()).filter(|n| *n != 0), + column_id: Some(field.column_id()).filter(|n| *n != 0), + r#type: type_, + }; + columns.push(column); + } + } + + Ok(Self::Final(columns)) + } +} + +pub async fn query_with_param_types<'a, P, I>( + client: &Arc, + query: &str, + params: I, +) -> Result +where + P: BorrowToSql, + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + let (params, param_types): (Vec<_>, Vec<_>) = params.into_iter().unzip(); + + let params = params.into_iter(); + + let param_oids = param_types.iter().map(|t| t.oid()).collect::>(); + + let params = params.into_iter(); + + let buf = client.with_buf(|buf| { + frontend::parse("", query, param_oids.into_iter(), buf).map_err(Error::parse)?; + + encode_bind_with_statement_name_and_param_types("", ¶m_types, params, "", buf)?; + + frontend::describe(b'S', "", buf).map_err(Error::encode)?; + + frontend::execute("", 0, buf).map_err(Error::encode)?; + + frontend::sync(buf); + + Ok(buf.split().freeze()) + })?; + + let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?; + + let mut state = QueryProcessingState::Empty; + + loop { + let message = responses.next().await?; + + state = state.process_message(client, message).await?; + + if let QueryProcessingState::Final(columns) = state { + return Ok(RowStream { + statement: Statement::unnamed(vec![], columns), + responses, + rows_affected: None, + _p: PhantomPinned, + }); + } + } +} + pub async fn query_portal( client: &InnerClient, portal: &Portal, @@ -164,7 +280,27 @@ where I: IntoIterator, I::IntoIter: ExactSizeIterator, { - let param_types = statement.params(); + encode_bind_with_statement_name_and_param_types( + statement.name(), + statement.params(), + params, + portal, + buf, + ) +} + +fn encode_bind_with_statement_name_and_param_types( + statement_name: &str, + param_types: &[Type], + params: I, + portal: &str, + buf: &mut BytesMut, +) -> Result<(), Error> +where + P: BorrowToSql, + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ let params = params.into_iter(); if param_types.len() != params.len() { @@ -181,7 +317,7 @@ where let mut error_idx = 0; let r = frontend::bind( portal, - statement.name(), + statement_name, param_formats, params.zip(param_types).enumerate(), |(idx, (param, ty)), buf| match param.borrow_to_sql().to_sql_checked(ty, buf) { diff --git a/tokio-postgres/src/statement.rs b/tokio-postgres/src/statement.rs index c5d657738..2b88ecd3b 100644 --- a/tokio-postgres/src/statement.rs +++ b/tokio-postgres/src/statement.rs @@ -14,6 +14,10 @@ struct StatementInner { impl Drop for StatementInner { fn drop(&mut self) { + if self.name.is_empty() { + // 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(); @@ -46,6 +50,15 @@ impl Statement { })) } + pub(crate) fn unnamed(params: Vec, columns: Vec) -> Statement { + Statement(Arc::new(StatementInner { + client: Weak::new(), + name: String::new(), + params, + columns, + })) + } + pub(crate) fn name(&self) -> &str { &self.0.name } diff --git a/tokio-postgres/src/transaction.rs b/tokio-postgres/src/transaction.rs index 96a324652..5a6094b56 100644 --- a/tokio-postgres/src/transaction.rs +++ b/tokio-postgres/src/transaction.rs @@ -227,6 +227,33 @@ impl<'a> Transaction<'a> { query::query_portal(self.client.inner(), portal, max_rows).await } + /// Like `Client::query_with_param_types`. + pub async fn query_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.query_raw_with_param_types(statement, params) + .await? + .try_collect() + .await + } + + /// Like `Client::query_raw_with_param_types`. + pub async fn query_raw_with_param_types( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result { + fn slice_iter<'a>( + s: &'a [(&'a (dyn ToSql + Sync), Type)], + ) -> impl ExactSizeIterator + 'a { + s.iter() + .map(|(param, param_type)| (*param as _, param_type.clone())) + } + query::query_with_param_types(self.client.inner(), statement, slice_iter(params)).await + } + /// Like `Client::copy_in`. pub async fn copy_in(&self, statement: &T) -> Result, Error> where diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 737f46631..925c99206 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -952,3 +952,109 @@ async fn deferred_constraint() { .await .unwrap_err(); } + +#[tokio::test] +async fn query_with_param_types_no_transaction() { + let client = connect("user=postgres").await; + + client + .batch_execute( + " + CREATE TEMPORARY TABLE foo ( + name TEXT, + age INT + ); + INSERT INTO foo (name, age) VALUES ('alice', 20), ('bob', 30), ('carol', 40); + ", + ) + .await + .unwrap(); + + let rows: Vec = client + .query_with_param_types( + "SELECT name, age, 'literal', 5 FROM foo WHERE name <> $1 AND age < $2 ORDER BY age", + &[(&"alice", Type::TEXT), (&50i32, Type::INT4)], + ) + .await + .unwrap(); + + assert_eq!(rows.len(), 2); + let first_row = &rows[0]; + assert_eq!(first_row.get::<_, &str>(0), "bob"); + assert_eq!(first_row.get::<_, i32>(1), 30); + assert_eq!(first_row.get::<_, &str>(2), "literal"); + assert_eq!(first_row.get::<_, i32>(3), 5); + + let second_row = &rows[1]; + assert_eq!(second_row.get::<_, &str>(0), "carol"); + assert_eq!(second_row.get::<_, i32>(1), 40); + assert_eq!(second_row.get::<_, &str>(2), "literal"); + assert_eq!(second_row.get::<_, i32>(3), 5); +} + +#[tokio::test] +async fn query_with_param_types_with_transaction() { + let mut client = connect("user=postgres").await; + + client + .batch_execute( + " + CREATE TEMPORARY TABLE foo ( + name TEXT, + age INT + ); + ", + ) + .await + .unwrap(); + + let transaction = client.transaction().await.unwrap(); + + let rows: Vec = transaction + .query_with_param_types( + "INSERT INTO foo (name, age) VALUES ($1, $2), ($3, $4), ($5, $6) returning name, age", + &[ + (&"alice", Type::TEXT), + (&20i32, Type::INT4), + (&"bob", Type::TEXT), + (&30i32, Type::INT4), + (&"carol", Type::TEXT), + (&40i32, Type::INT4), + ], + ) + .await + .unwrap(); + let inserted_values: Vec<(String, i32)> = rows + .iter() + .map(|row| (row.get::<_, String>(0), row.get::<_, i32>(1))) + .collect(); + assert_eq!( + inserted_values, + [ + ("alice".to_string(), 20), + ("bob".to_string(), 30), + ("carol".to_string(), 40) + ] + ); + + let rows: Vec = transaction + .query_with_param_types( + "SELECT name, age, 'literal', 5 FROM foo WHERE name <> $1 AND age < $2 ORDER BY age", + &[(&"alice", Type::TEXT), (&50i32, Type::INT4)], + ) + .await + .unwrap(); + + assert_eq!(rows.len(), 2); + let first_row = &rows[0]; + assert_eq!(first_row.get::<_, &str>(0), "bob"); + assert_eq!(first_row.get::<_, i32>(1), 30); + assert_eq!(first_row.get::<_, &str>(2), "literal"); + assert_eq!(first_row.get::<_, i32>(3), 5); + + let second_row = &rows[1]; + assert_eq!(second_row.get::<_, &str>(0), "carol"); + assert_eq!(second_row.get::<_, i32>(1), 40); + assert_eq!(second_row.get::<_, &str>(2), "literal"); + assert_eq!(second_row.get::<_, i32>(3), 5); +} From 84994dad1aa9c3ef5c813b95c86c80dbfa4b7f0d Mon Sep 17 00:00:00 2001 From: Lev Kokotov Date: Sat, 6 Jul 2024 11:23:26 -0400 Subject: [PATCH 132/207] Derive Clone for Row --- postgres-protocol/src/message/backend.rs | 2 +- tokio-postgres/src/row.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/postgres-protocol/src/message/backend.rs b/postgres-protocol/src/message/backend.rs index 1b5be1098..c4439b26a 100644 --- a/postgres-protocol/src/message/backend.rs +++ b/postgres-protocol/src/message/backend.rs @@ -524,7 +524,7 @@ impl CopyOutResponseBody { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DataRowBody { storage: Bytes, len: u16, diff --git a/tokio-postgres/src/row.rs b/tokio-postgres/src/row.rs index 3c79de603..767c26921 100644 --- a/tokio-postgres/src/row.rs +++ b/tokio-postgres/src/row.rs @@ -95,6 +95,7 @@ where } /// A row of data returned from the database by a query. +#[derive(Clone)] pub struct Row { statement: Statement, body: DataRowBody, From 2b1949dd2f8745fcfaefe4b5e228684c25997265 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sat, 6 Jul 2024 11:00:41 -0700 Subject: [PATCH 133/207] impl Debug for Statement The lack of this common trait bound caused some unpleasantness. For example, the following didn't compile: let x = OnceLock::new(); let stmt = db.prepare(...)?; x.set(stmt).expect(...); // returns Result<(), T=Statement> where T: Debug --- tokio-postgres/src/statement.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tokio-postgres/src/statement.rs b/tokio-postgres/src/statement.rs index c5d657738..4955d3b41 100644 --- a/tokio-postgres/src/statement.rs +++ b/tokio-postgres/src/statement.rs @@ -61,6 +61,16 @@ impl Statement { } } +impl std::fmt::Debug for Statement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.debug_struct("Statement") + .field("name", &self.0.name) + .field("params", &self.0.params) + .field("columns", &self.0.columns) + .finish_non_exhaustive() + } +} + /// Information about a column of a query. #[derive(Debug)] pub struct Column { From 1f312194928c5a385d51d52e5d13ca59d3dc1b43 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sat, 6 Jul 2024 12:29:09 -0700 Subject: [PATCH 134/207] Fix a few nits pointed out by clippy - ...::max_value() -> ..::MAX - delete explicit import of signed integer types --- postgres-protocol/src/lib.rs | 2 +- postgres-types/src/lib.rs | 2 +- postgres-types/src/special.rs | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/postgres-protocol/src/lib.rs b/postgres-protocol/src/lib.rs index 83d9bf55c..e0de3b6c6 100644 --- a/postgres-protocol/src/lib.rs +++ b/postgres-protocol/src/lib.rs @@ -60,7 +60,7 @@ macro_rules! from_usize { impl FromUsize for $t { #[inline] fn from_usize(x: usize) -> io::Result<$t> { - if x > <$t>::max_value() as usize { + if x > <$t>::MAX as usize { Err(io::Error::new( io::ErrorKind::InvalidInput, "value too large to transmit", diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index 2f02f6e5f..492039766 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -1222,7 +1222,7 @@ impl ToSql for IpAddr { } fn downcast(len: usize) -> Result> { - if len > i32::max_value() as usize { + if len > i32::MAX as usize { Err("value too large to transmit".into()) } else { Ok(len as i32) diff --git a/postgres-types/src/special.rs b/postgres-types/src/special.rs index 1a865287e..d8541bf0e 100644 --- a/postgres-types/src/special.rs +++ b/postgres-types/src/special.rs @@ -1,7 +1,6 @@ use bytes::BytesMut; use postgres_protocol::types; use std::error::Error; -use std::{i32, i64}; use crate::{FromSql, IsNull, ToSql, Type}; From 263b0068af39072bc7be05b6500e47f263cbd43e Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 6 Jul 2024 19:21:37 -0400 Subject: [PATCH 135/207] Handle non-UTF8 error fields --- postgres-protocol/src/message/backend.rs | 10 +++++-- tokio-postgres/src/error/mod.rs | 37 ++++++++++++------------ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/postgres-protocol/src/message/backend.rs b/postgres-protocol/src/message/backend.rs index c4439b26a..73b169288 100644 --- a/postgres-protocol/src/message/backend.rs +++ b/postgres-protocol/src/message/backend.rs @@ -633,7 +633,7 @@ impl<'a> FallibleIterator for ErrorFields<'a> { } let value_end = find_null(self.buf, 0)?; - let value = get_str(&self.buf[..value_end])?; + let value = &self.buf[..value_end]; self.buf = &self.buf[value_end + 1..]; Ok(Some(ErrorField { type_, value })) @@ -642,7 +642,7 @@ impl<'a> FallibleIterator for ErrorFields<'a> { pub struct ErrorField<'a> { type_: u8, - value: &'a str, + value: &'a [u8], } impl<'a> ErrorField<'a> { @@ -652,7 +652,13 @@ impl<'a> ErrorField<'a> { } #[inline] + #[deprecated(note = "use value_bytes instead", since = "0.6.7")] pub fn value(&self) -> &str { + str::from_utf8(self.value).expect("error field value contained non-UTF8 bytes") + } + + #[inline] + pub fn value_bytes(&self) -> &[u8] { self.value } } diff --git a/tokio-postgres/src/error/mod.rs b/tokio-postgres/src/error/mod.rs index f1e2644c6..75664d258 100644 --- a/tokio-postgres/src/error/mod.rs +++ b/tokio-postgres/src/error/mod.rs @@ -107,14 +107,15 @@ impl DbError { let mut routine = None; while let Some(field) = fields.next()? { + let value = String::from_utf8_lossy(field.value_bytes()); match field.type_() { - b'S' => severity = Some(field.value().to_owned()), - b'C' => code = Some(SqlState::from_code(field.value())), - b'M' => message = Some(field.value().to_owned()), - b'D' => detail = Some(field.value().to_owned()), - b'H' => hint = Some(field.value().to_owned()), + b'S' => severity = Some(value.into_owned()), + b'C' => code = Some(SqlState::from_code(&value)), + b'M' => message = Some(value.into_owned()), + b'D' => detail = Some(value.into_owned()), + b'H' => hint = Some(value.into_owned()), b'P' => { - normal_position = Some(field.value().parse::().map_err(|_| { + normal_position = Some(value.parse::().map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, "`P` field did not contain an integer", @@ -122,32 +123,32 @@ impl DbError { })?); } b'p' => { - internal_position = Some(field.value().parse::().map_err(|_| { + internal_position = Some(value.parse::().map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, "`p` field did not contain an integer", ) })?); } - b'q' => internal_query = Some(field.value().to_owned()), - b'W' => where_ = Some(field.value().to_owned()), - b's' => schema = Some(field.value().to_owned()), - b't' => table = Some(field.value().to_owned()), - b'c' => column = Some(field.value().to_owned()), - b'd' => datatype = Some(field.value().to_owned()), - b'n' => constraint = Some(field.value().to_owned()), - b'F' => file = Some(field.value().to_owned()), + b'q' => internal_query = Some(value.into_owned()), + b'W' => where_ = Some(value.into_owned()), + b's' => schema = Some(value.into_owned()), + b't' => table = Some(value.into_owned()), + b'c' => column = Some(value.into_owned()), + b'd' => datatype = Some(value.into_owned()), + b'n' => constraint = Some(value.into_owned()), + b'F' => file = Some(value.into_owned()), b'L' => { - line = Some(field.value().parse::().map_err(|_| { + line = Some(value.parse::().map_err(|_| { io::Error::new( io::ErrorKind::InvalidInput, "`L` field did not contain an integer", ) })?); } - b'R' => routine = Some(field.value().to_owned()), + b'R' => routine = Some(value.into_owned()), b'V' => { - parsed_severity = Some(Severity::from_str(field.value()).ok_or_else(|| { + parsed_severity = Some(Severity::from_str(&value).ok_or_else(|| { io::Error::new( io::ErrorKind::InvalidInput, "`V` field contained an invalid value", From cfd91632be877543a7e19e7a05816ed5d241b559 Mon Sep 17 00:00:00 2001 From: Dane Rigby Date: Sun, 7 Jul 2024 13:56:35 -0500 Subject: [PATCH 136/207] PR Fix: Only use single clone for RowDescription --- tokio-postgres/src/simple_query.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tokio-postgres/src/simple_query.rs b/tokio-postgres/src/simple_query.rs index 86af8e739..b6500260e 100644 --- a/tokio-postgres/src/simple_query.rs +++ b/tokio-postgres/src/simple_query.rs @@ -101,9 +101,9 @@ impl Stream for SimpleQueryStream { .map_err(Error::parse)? .into(); - *this.columns = Some(columns.clone()); + *this.columns = Some(columns); Poll::Ready(Some(Ok(SimpleQueryMessage::RowDescription( - columns.clone(), + this.columns.as_ref().unwrap().clone(), )))) } Message::DataRow(body) => { From 3f8f5ded337a0122959f6e4a3dc9343bf6c6ee70 Mon Sep 17 00:00:00 2001 From: Ramnivas Laddad Date: Sun, 7 Jul 2024 16:21:40 -0700 Subject: [PATCH 137/207] Replace the state machine to process messages with a direct match statements --- tokio-postgres/src/query.rs | 101 ++++++++++-------------------------- 1 file changed, 27 insertions(+), 74 deletions(-) diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index b9cc66405..2bdfa14cc 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -9,7 +9,7 @@ use fallible_iterator::FallibleIterator; use futures_util::{ready, Stream}; use log::{debug, log_enabled, Level}; use pin_project_lite::pin_project; -use postgres_protocol::message::backend::{CommandCompleteBody, Message, RowDescriptionBody}; +use postgres_protocol::message::backend::{CommandCompleteBody, Message}; use postgres_protocol::message::frontend; use postgres_types::Type; use std::fmt; @@ -61,66 +61,6 @@ where }) } -enum QueryProcessingState { - Empty, - ParseCompleted, - BindCompleted, - ParameterDescribed, - Final(Vec), -} - -/// State machine for processing messages for `query_with_param_types`. -impl QueryProcessingState { - pub async fn process_message( - self, - client: &Arc, - message: Message, - ) -> Result { - match (self, message) { - (QueryProcessingState::Empty, Message::ParseComplete) => { - Ok(QueryProcessingState::ParseCompleted) - } - (QueryProcessingState::ParseCompleted, Message::BindComplete) => { - Ok(QueryProcessingState::BindCompleted) - } - (QueryProcessingState::BindCompleted, Message::ParameterDescription(_)) => { - Ok(QueryProcessingState::ParameterDescribed) - } - ( - QueryProcessingState::ParameterDescribed, - Message::RowDescription(row_description), - ) => Self::form_final(client, Some(row_description)).await, - (QueryProcessingState::ParameterDescribed, Message::NoData) => { - Self::form_final(client, None).await - } - (_, Message::ErrorResponse(body)) => Err(Error::db(body)), - _ => Err(Error::unexpected_message()), - } - } - - async fn form_final( - client: &Arc, - row_description: Option, - ) -> Result { - let mut columns = vec![]; - if let Some(row_description) = row_description { - let mut it = row_description.fields(); - while let Some(field) = it.next().map_err(Error::parse)? { - let type_ = get_type(client, field.type_oid()).await?; - let column = Column { - name: field.name().to_string(), - table_oid: Some(field.table_oid()).filter(|n| *n != 0), - column_id: Some(field.column_id()).filter(|n| *n != 0), - r#type: type_, - }; - columns.push(column); - } - } - - Ok(Self::Final(columns)) - } -} - pub async fn query_with_param_types<'a, P, I>( client: &Arc, query: &str, @@ -155,20 +95,33 @@ where let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?; - let mut state = QueryProcessingState::Empty; - loop { - let message = responses.next().await?; - - state = state.process_message(client, message).await?; - - if let QueryProcessingState::Final(columns) = state { - return Ok(RowStream { - statement: Statement::unnamed(vec![], columns), - responses, - rows_affected: None, - _p: PhantomPinned, - }); + match responses.next().await? { + Message::ParseComplete + | Message::BindComplete + | Message::ParameterDescription(_) + | Message::NoData => {} + Message::RowDescription(row_description) => { + let mut columns: Vec = vec![]; + let mut it = row_description.fields(); + while let Some(field) = it.next().map_err(Error::parse)? { + let type_ = get_type(client, field.type_oid()).await?; + let column = Column { + name: field.name().to_string(), + table_oid: Some(field.table_oid()).filter(|n| *n != 0), + column_id: Some(field.column_id()).filter(|n| *n != 0), + r#type: type_, + }; + columns.push(column); + } + return Ok(RowStream { + statement: Statement::unnamed(vec![], columns), + responses, + rows_affected: None, + _p: PhantomPinned, + }); + } + _ => return Err(Error::unexpected_message()), } } } From 74eb4dbf7399cb96500f2b60a2b838805471a26a Mon Sep 17 00:00:00 2001 From: Ramnivas Laddad Date: Sun, 7 Jul 2024 16:43:41 -0700 Subject: [PATCH 138/207] Remove query_raw_with_param_types as per PR feedback --- tokio-postgres/src/client.rs | 56 ++++++---------------------- tokio-postgres/src/generic_client.rs | 23 ------------ tokio-postgres/src/transaction.rs | 20 +--------- 3 files changed, 12 insertions(+), 87 deletions(-) diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index 431bfa792..e420bcf2f 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -366,8 +366,13 @@ impl Client { /// Like `query`, but requires the types of query parameters to be explicitly specified. /// - /// Compared to `query`, this method allows performing queries without three round trips (for prepare, execute, and close). Thus, - /// this is suitable in environments where prepared statements aren't supported (such as Cloudflare Workers with Hyperdrive). + /// Compared to `query`, this method allows performing queries without three round trips (for + /// prepare, execute, and close) by requiring the caller to specify parameter values along with + /// their Postgres type. Thus, this is suitable in environments where prepared statements aren't + /// supported (such as Cloudflare Workers with Hyperdrive). + /// + /// A statement may contain parameters, specified by `$n`, where `n` is the index of the + /// parameter of the list provided, 1-indexed. /// /// # Examples /// @@ -394,48 +399,6 @@ impl Client { statement: &str, params: &[(&(dyn ToSql + Sync), Type)], ) -> Result, Error> { - self.query_raw_with_param_types(statement, params) - .await? - .try_collect() - .await - } - - /// The maximally flexible version of [`query_with_param_types`]. - /// - /// A statement may contain parameters, specified by `$n`, where `n` is the index of the parameter of the list - /// provided, 1-indexed. - /// - /// The parameters must specify value along with their Postgres type. This allows performing - /// queries without three round trips (for prepare, execute, and close). - /// - /// [`query_with_param_types`]: #method.query_with_param_types - /// - /// # Examples - /// - /// ```no_run - /// # async fn async_main(client: &tokio_postgres::Client) -> Result<(), tokio_postgres::Error> { - /// use tokio_postgres::types::ToSql; - /// use tokio_postgres::types::Type; - /// use futures_util::{pin_mut, TryStreamExt}; - /// - /// let mut it = client.query_raw_with_param_types( - /// "SELECT foo FROM bar WHERE biz = $1 AND baz = $2", - /// &[(&"first param", Type::TEXT), (&2i32, Type::INT4)], - /// ).await?; - /// - /// pin_mut!(it); - /// while let Some(row) = it.try_next().await? { - /// let foo: i32 = row.get("foo"); - /// println!("foo: {}", foo); - /// } - /// # Ok(()) - /// # } - /// ``` - pub async fn query_raw_with_param_types( - &self, - statement: &str, - params: &[(&(dyn ToSql + Sync), Type)], - ) -> Result { fn slice_iter<'a>( s: &'a [(&'a (dyn ToSql + Sync), Type)], ) -> impl ExactSizeIterator + 'a { @@ -443,7 +406,10 @@ impl Client { .map(|(param, param_type)| (*param as _, param_type.clone())) } - query::query_with_param_types(&self.inner, statement, slice_iter(params)).await + query::query_with_param_types(&self.inner, statement, slice_iter(params)) + .await? + .try_collect() + .await } /// Executes a statement, returning the number of rows modified. diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index 3a0b09233..b892015dc 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -63,13 +63,6 @@ pub trait GenericClient: private::Sealed { params: &[(&(dyn ToSql + Sync), Type)], ) -> Result, Error>; - /// Like `Client::query_raw_with_param_types`. - async fn query_raw_with_param_types( - &self, - statement: &str, - params: &[(&(dyn ToSql + Sync), Type)], - ) -> Result; - /// Like `Client::prepare`. async fn prepare(&self, query: &str) -> Result; @@ -158,14 +151,6 @@ impl GenericClient for Client { self.query_with_param_types(statement, params).await } - async fn query_raw_with_param_types( - &self, - statement: &str, - params: &[(&(dyn ToSql + Sync), Type)], - ) -> Result { - self.query_raw_with_param_types(statement, params).await - } - async fn prepare(&self, query: &str) -> Result { self.prepare(query).await } @@ -260,14 +245,6 @@ impl GenericClient for Transaction<'_> { self.query_with_param_types(statement, params).await } - async fn query_raw_with_param_types( - &self, - statement: &str, - params: &[(&(dyn ToSql + Sync), Type)], - ) -> Result { - self.query_raw_with_param_types(statement, params).await - } - async fn prepare(&self, query: &str) -> Result { self.prepare(query).await } diff --git a/tokio-postgres/src/transaction.rs b/tokio-postgres/src/transaction.rs index 5a6094b56..8a0ad2224 100644 --- a/tokio-postgres/src/transaction.rs +++ b/tokio-postgres/src/transaction.rs @@ -233,25 +233,7 @@ impl<'a> Transaction<'a> { statement: &str, params: &[(&(dyn ToSql + Sync), Type)], ) -> Result, Error> { - self.query_raw_with_param_types(statement, params) - .await? - .try_collect() - .await - } - - /// Like `Client::query_raw_with_param_types`. - pub async fn query_raw_with_param_types( - &self, - statement: &str, - params: &[(&(dyn ToSql + Sync), Type)], - ) -> Result { - fn slice_iter<'a>( - s: &'a [(&'a (dyn ToSql + Sync), Type)], - ) -> impl ExactSizeIterator + 'a { - s.iter() - .map(|(param, param_type)| (*param as _, param_type.clone())) - } - query::query_with_param_types(self.client.inner(), statement, slice_iter(params)).await + self.client.query_with_param_types(statement, params).await } /// Like `Client::copy_in`. From 2647024c660ca27701898325a8772b83bece4982 Mon Sep 17 00:00:00 2001 From: Dane Rigby Date: Sun, 7 Jul 2024 21:30:23 -0500 Subject: [PATCH 139/207] PR Fix: Clone first then move --- tokio-postgres/src/simple_query.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tokio-postgres/src/simple_query.rs b/tokio-postgres/src/simple_query.rs index b6500260e..24473b896 100644 --- a/tokio-postgres/src/simple_query.rs +++ b/tokio-postgres/src/simple_query.rs @@ -101,10 +101,8 @@ impl Stream for SimpleQueryStream { .map_err(Error::parse)? .into(); - *this.columns = Some(columns); - Poll::Ready(Some(Ok(SimpleQueryMessage::RowDescription( - this.columns.as_ref().unwrap().clone(), - )))) + *this.columns = Some(columns.clone()); + Poll::Ready(Some(Ok(SimpleQueryMessage::RowDescription(columns)))) } Message::DataRow(body) => { let row = match &this.columns { From dbd4d02e2f3a367b949e356e9dda40c08272d954 Mon Sep 17 00:00:00 2001 From: Ramnivas Laddad Date: Mon, 8 Jul 2024 17:21:32 -0700 Subject: [PATCH 140/207] Address review comment to rename query_with_param_types to query_typed --- tokio-postgres/src/client.rs | 6 +++--- tokio-postgres/src/generic_client.rs | 12 ++++++------ tokio-postgres/src/query.rs | 2 +- tokio-postgres/src/transaction.rs | 6 +++--- tokio-postgres/tests/test/main.rs | 10 +++++----- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index e420bcf2f..2b29351a5 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -382,7 +382,7 @@ impl Client { /// use tokio_postgres::types::Type; /// use futures_util::{pin_mut, TryStreamExt}; /// - /// let rows = client.query_with_param_types( + /// let rows = client.query_typed( /// "SELECT foo FROM bar WHERE biz = $1 AND baz = $2", /// &[(&"first param", Type::TEXT), (&2i32, Type::INT4)], /// ).await?; @@ -394,7 +394,7 @@ impl Client { /// # Ok(()) /// # } /// ``` - pub async fn query_with_param_types( + pub async fn query_typed( &self, statement: &str, params: &[(&(dyn ToSql + Sync), Type)], @@ -406,7 +406,7 @@ impl Client { .map(|(param, param_type)| (*param as _, param_type.clone())) } - query::query_with_param_types(&self.inner, statement, slice_iter(params)) + query::query_typed(&self.inner, statement, slice_iter(params)) .await? .try_collect() .await diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index e43bddfea..b91d78064 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -56,8 +56,8 @@ pub trait GenericClient: private::Sealed { I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator; - /// Like [`Client::query_with_param_types`] - async fn query_with_param_types( + /// Like [`Client::query_typed`] + async fn query_typed( &self, statement: &str, params: &[(&(dyn ToSql + Sync), Type)], @@ -146,12 +146,12 @@ impl GenericClient for Client { self.query_raw(statement, params).await } - async fn query_with_param_types( + async fn query_typed( &self, statement: &str, params: &[(&(dyn ToSql + Sync), Type)], ) -> Result, Error> { - self.query_with_param_types(statement, params).await + self.query_typed(statement, params).await } async fn prepare(&self, query: &str) -> Result { @@ -244,12 +244,12 @@ impl GenericClient for Transaction<'_> { self.query_raw(statement, params).await } - async fn query_with_param_types( + async fn query_typed( &self, statement: &str, params: &[(&(dyn ToSql + Sync), Type)], ) -> Result, Error> { - self.query_with_param_types(statement, params).await + self.query_typed(statement, params).await } async fn prepare(&self, query: &str) -> Result { diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index 2bdfa14cc..b54e095df 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -61,7 +61,7 @@ where }) } -pub async fn query_with_param_types<'a, P, I>( +pub async fn query_typed<'a, P, I>( client: &Arc, query: &str, params: I, diff --git a/tokio-postgres/src/transaction.rs b/tokio-postgres/src/transaction.rs index 8a0ad2224..3e62b2ac7 100644 --- a/tokio-postgres/src/transaction.rs +++ b/tokio-postgres/src/transaction.rs @@ -227,13 +227,13 @@ impl<'a> Transaction<'a> { query::query_portal(self.client.inner(), portal, max_rows).await } - /// Like `Client::query_with_param_types`. - pub async fn query_with_param_types( + /// Like `Client::query_typed`. + pub async fn query_typed( &self, statement: &str, params: &[(&(dyn ToSql + Sync), Type)], ) -> Result, Error> { - self.client.query_with_param_types(statement, params).await + self.client.query_typed(statement, params).await } /// Like `Client::copy_in`. diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 925c99206..7ddb7a36a 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -954,7 +954,7 @@ async fn deferred_constraint() { } #[tokio::test] -async fn query_with_param_types_no_transaction() { +async fn query_typed_no_transaction() { let client = connect("user=postgres").await; client @@ -971,7 +971,7 @@ async fn query_with_param_types_no_transaction() { .unwrap(); let rows: Vec = client - .query_with_param_types( + .query_typed( "SELECT name, age, 'literal', 5 FROM foo WHERE name <> $1 AND age < $2 ORDER BY age", &[(&"alice", Type::TEXT), (&50i32, Type::INT4)], ) @@ -993,7 +993,7 @@ async fn query_with_param_types_no_transaction() { } #[tokio::test] -async fn query_with_param_types_with_transaction() { +async fn query_typed_with_transaction() { let mut client = connect("user=postgres").await; client @@ -1011,7 +1011,7 @@ async fn query_with_param_types_with_transaction() { let transaction = client.transaction().await.unwrap(); let rows: Vec = transaction - .query_with_param_types( + .query_typed( "INSERT INTO foo (name, age) VALUES ($1, $2), ($3, $4), ($5, $6) returning name, age", &[ (&"alice", Type::TEXT), @@ -1038,7 +1038,7 @@ async fn query_with_param_types_with_transaction() { ); let rows: Vec = transaction - .query_with_param_types( + .query_typed( "SELECT name, age, 'literal', 5 FROM foo WHERE name <> $1 AND age < $2 ORDER BY age", &[(&"alice", Type::TEXT), (&50i32, Type::INT4)], ) From 0fa32471ef2e20b7f2e554d6d97cde3a67f1d494 Mon Sep 17 00:00:00 2001 From: Ramnivas Laddad Date: Tue, 9 Jul 2024 17:59:39 -0700 Subject: [PATCH 141/207] Fix a clippy warning --- tokio-postgres/src/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index b54e095df..e304bbaea 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -54,7 +54,7 @@ where }; let responses = start(client, buf).await?; Ok(RowStream { - statement: statement, + statement, responses, rows_affected: None, _p: PhantomPinned, From 71c836b980799256a7f266195382fc8449fca5e4 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 13 Jul 2024 20:45:32 -0400 Subject: [PATCH 142/207] query_typed tweaks --- postgres/src/client.rs | 65 ++++++++++++++++++++++++++++ postgres/src/generic_client.rs | 45 +++++++++++++++++++ postgres/src/transaction.rs | 29 +++++++++++++ tokio-postgres/src/client.rs | 63 +++++++++++++++++---------- tokio-postgres/src/generic_client.rs | 22 ++++++++++ tokio-postgres/src/query.rs | 63 +++++++++++---------------- tokio-postgres/src/transaction.rs | 27 ++++++++---- 7 files changed, 243 insertions(+), 71 deletions(-) diff --git a/postgres/src/client.rs b/postgres/src/client.rs index c8e14cf81..42ce6dec9 100644 --- a/postgres/src/client.rs +++ b/postgres/src/client.rs @@ -257,6 +257,71 @@ impl Client { Ok(RowIter::new(self.connection.as_ref(), stream)) } + /// Like `query`, but requires the types of query parameters to be explicitly specified. + /// + /// Compared to `query`, this method allows performing queries without three round trips (for + /// prepare, execute, and close) by requiring the caller to specify parameter values along with + /// their Postgres type. Thus, this is suitable in environments where prepared statements aren't + /// supported (such as Cloudflare Workers with Hyperdrive). + /// + /// A statement may contain parameters, specified by `$n`, where `n` is the index of the + /// parameter of the list provided, 1-indexed. + pub fn query_typed( + &mut self, + query: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.connection + .block_on(self.client.query_typed(query, params)) + } + + /// The maximally flexible version of [`query_typed`]. + /// + /// Compared to `query`, this method allows performing queries without three round trips (for + /// prepare, execute, and close) by requiring the caller to specify parameter values along with + /// their Postgres type. Thus, this is suitable in environments where prepared statements aren't + /// supported (such as Cloudflare Workers with Hyperdrive). + /// + /// A statement may contain parameters, specified by `$n`, where `n` is the index of the + /// parameter of the list provided, 1-indexed. + /// + /// [`query_typed`]: #method.query_typed + /// + /// # Examples + /// ```no_run + /// # use postgres::{Client, NoTls}; + /// use postgres::types::{ToSql, Type}; + /// use fallible_iterator::FallibleIterator; + /// # fn main() -> Result<(), postgres::Error> { + /// # let mut client = Client::connect("host=localhost user=postgres", NoTls)?; + /// + /// let params: Vec<(String, Type)> = vec![ + /// ("first param".into(), Type::TEXT), + /// ("second param".into(), Type::TEXT), + /// ]; + /// let mut it = client.query_typed_raw( + /// "SELECT foo FROM bar WHERE biz = $1 AND baz = $2", + /// params, + /// )?; + /// + /// while let Some(row) = it.next()? { + /// let foo: i32 = row.get("foo"); + /// println!("foo: {}", foo); + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn query_typed_raw(&mut self, query: &str, params: I) -> Result, Error> + where + P: BorrowToSql, + I: IntoIterator, + { + let stream = self + .connection + .block_on(self.client.query_typed_raw(query, params))?; + Ok(RowIter::new(self.connection.as_ref(), stream)) + } + /// Creates a new prepared statement. /// /// Prepared statements can be executed repeatedly, and may contain query parameters (indicated by `$1`, `$2`, etc), diff --git a/postgres/src/generic_client.rs b/postgres/src/generic_client.rs index 12f07465d..7b534867c 100644 --- a/postgres/src/generic_client.rs +++ b/postgres/src/generic_client.rs @@ -44,6 +44,19 @@ pub trait GenericClient: private::Sealed { I: IntoIterator, I::IntoIter: ExactSizeIterator; + /// Like [`Client::query_typed`] + fn query_typed( + &mut self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error>; + + /// Like [`Client::query_typed_raw`] + fn query_typed_raw(&mut self, statement: &str, params: I) -> Result, Error> + where + P: BorrowToSql, + I: IntoIterator + Sync + Send; + /// Like `Client::prepare`. fn prepare(&mut self, query: &str) -> Result; @@ -115,6 +128,22 @@ impl GenericClient for Client { self.query_raw(query, params) } + fn query_typed( + &mut self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.query_typed(statement, params) + } + + fn query_typed_raw(&mut self, statement: &str, params: I) -> Result, Error> + where + P: BorrowToSql, + I: IntoIterator + Sync + Send, + { + self.query_typed_raw(statement, params) + } + fn prepare(&mut self, query: &str) -> Result { self.prepare(query) } @@ -195,6 +224,22 @@ impl GenericClient for Transaction<'_> { self.query_raw(query, params) } + fn query_typed( + &mut self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.query_typed(statement, params) + } + + fn query_typed_raw(&mut self, statement: &str, params: I) -> Result, Error> + where + P: BorrowToSql, + I: IntoIterator + Sync + Send, + { + self.query_typed_raw(statement, params) + } + fn prepare(&mut self, query: &str) -> Result { self.prepare(query) } diff --git a/postgres/src/transaction.rs b/postgres/src/transaction.rs index 17c49c406..5c8c15973 100644 --- a/postgres/src/transaction.rs +++ b/postgres/src/transaction.rs @@ -115,6 +115,35 @@ impl<'a> Transaction<'a> { Ok(RowIter::new(self.connection.as_ref(), stream)) } + /// Like `Client::query_typed`. + pub fn query_typed( + &mut self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.connection.block_on( + self.transaction + .as_ref() + .unwrap() + .query_typed(statement, params), + ) + } + + /// Like `Client::query_typed_raw`. + pub fn query_typed_raw(&mut self, query: &str, params: I) -> Result, Error> + where + P: BorrowToSql, + I: IntoIterator, + { + let stream = self.connection.block_on( + self.transaction + .as_ref() + .unwrap() + .query_typed_raw(query, params), + )?; + Ok(RowIter::new(self.connection.as_ref(), stream)) + } + /// Binds parameters to a statement, creating a "portal". /// /// Portals can be used with the `query_portal` method to page through the results of a query without being forced diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index 2b29351a5..b04f05f88 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -333,7 +333,6 @@ impl Client { /// /// ```no_run /// # async fn async_main(client: &tokio_postgres::Client) -> Result<(), tokio_postgres::Error> { - /// use tokio_postgres::types::ToSql; /// use futures_util::{pin_mut, TryStreamExt}; /// /// let params: Vec = vec![ @@ -373,43 +372,59 @@ impl Client { /// /// A statement may contain parameters, specified by `$n`, where `n` is the index of the /// parameter of the list provided, 1-indexed. + pub async fn query_typed( + &self, + query: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.query_typed_raw(query, params.iter().map(|(v, t)| (*v, t.clone()))) + .await? + .try_collect() + .await + } + + /// The maximally flexible version of [`query_typed`]. + /// + /// Compared to `query`, this method allows performing queries without three round trips (for + /// prepare, execute, and close) by requiring the caller to specify parameter values along with + /// their Postgres type. Thus, this is suitable in environments where prepared statements aren't + /// supported (such as Cloudflare Workers with Hyperdrive). + /// + /// A statement may contain parameters, specified by `$n`, where `n` is the index of the + /// parameter of the list provided, 1-indexed. + /// + /// [`query_typed`]: #method.query_typed /// /// # Examples /// /// ```no_run /// # async fn async_main(client: &tokio_postgres::Client) -> Result<(), tokio_postgres::Error> { - /// use tokio_postgres::types::ToSql; - /// use tokio_postgres::types::Type; /// use futures_util::{pin_mut, TryStreamExt}; + /// use tokio_postgres::types::Type; /// - /// let rows = client.query_typed( + /// let params: Vec<(String, Type)> = vec![ + /// ("first param".into(), Type::TEXT), + /// ("second param".into(), Type::TEXT), + /// ]; + /// let mut it = client.query_typed_raw( /// "SELECT foo FROM bar WHERE biz = $1 AND baz = $2", - /// &[(&"first param", Type::TEXT), (&2i32, Type::INT4)], + /// params, /// ).await?; /// - /// for row in rows { - /// let foo: i32 = row.get("foo"); - /// println!("foo: {}", foo); + /// pin_mut!(it); + /// while let Some(row) = it.try_next().await? { + /// let foo: i32 = row.get("foo"); + /// println!("foo: {}", foo); /// } /// # Ok(()) /// # } /// ``` - pub async fn query_typed( - &self, - statement: &str, - params: &[(&(dyn ToSql + Sync), Type)], - ) -> Result, Error> { - fn slice_iter<'a>( - s: &'a [(&'a (dyn ToSql + Sync), Type)], - ) -> impl ExactSizeIterator + 'a { - s.iter() - .map(|(param, param_type)| (*param as _, param_type.clone())) - } - - query::query_typed(&self.inner, statement, slice_iter(params)) - .await? - .try_collect() - .await + pub async fn query_typed_raw(&self, query: &str, params: I) -> Result + where + P: BorrowToSql, + I: IntoIterator, + { + query::query_typed(&self.inner, query, params).await } /// Executes a statement, returning the number of rows modified. diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index b91d78064..6e7dffeb1 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -63,6 +63,12 @@ pub trait GenericClient: private::Sealed { params: &[(&(dyn ToSql + Sync), Type)], ) -> Result, Error>; + /// Like [`Client::query_typed_raw`] + async fn query_typed_raw(&self, statement: &str, params: I) -> Result + where + P: BorrowToSql, + I: IntoIterator + Sync + Send; + /// Like [`Client::prepare`]. async fn prepare(&self, query: &str) -> Result; @@ -154,6 +160,14 @@ impl GenericClient for Client { self.query_typed(statement, params).await } + async fn query_typed_raw(&self, statement: &str, params: I) -> Result + where + P: BorrowToSql, + I: IntoIterator + Sync + Send, + { + self.query_typed_raw(statement, params).await + } + async fn prepare(&self, query: &str) -> Result { self.prepare(query).await } @@ -252,6 +266,14 @@ impl GenericClient for Transaction<'_> { self.query_typed(statement, params).await } + async fn query_typed_raw(&self, statement: &str, params: I) -> Result + where + P: BorrowToSql, + I: IntoIterator + Sync + Send, + { + self.query_typed_raw(statement, params).await + } + async fn prepare(&self, query: &str) -> Result { self.prepare(query).await } diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index e304bbaea..be42d66b6 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -69,29 +69,21 @@ pub async fn query_typed<'a, P, I>( where P: BorrowToSql, I: IntoIterator, - I::IntoIter: ExactSizeIterator, { - let (params, param_types): (Vec<_>, Vec<_>) = params.into_iter().unzip(); - - let params = params.into_iter(); - - let param_oids = param_types.iter().map(|t| t.oid()).collect::>(); - - let params = params.into_iter(); - - let buf = client.with_buf(|buf| { - frontend::parse("", query, param_oids.into_iter(), buf).map_err(Error::parse)?; - - encode_bind_with_statement_name_and_param_types("", ¶m_types, params, "", buf)?; - - frontend::describe(b'S', "", buf).map_err(Error::encode)?; - - frontend::execute("", 0, buf).map_err(Error::encode)?; + let buf = { + let params = params.into_iter().collect::>(); + let param_oids = params.iter().map(|(_, t)| t.oid()).collect::>(); - frontend::sync(buf); + client.with_buf(|buf| { + frontend::parse("", query, param_oids.into_iter(), buf).map_err(Error::parse)?; + encode_bind_raw("", params, "", buf)?; + frontend::describe(b'S', "", buf).map_err(Error::encode)?; + frontend::execute("", 0, buf).map_err(Error::encode)?; + frontend::sync(buf); - Ok(buf.split().freeze()) - })?; + Ok(buf.split().freeze()) + })? + }; let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?; @@ -233,47 +225,42 @@ where I: IntoIterator, I::IntoIter: ExactSizeIterator, { - encode_bind_with_statement_name_and_param_types( + let params = params.into_iter(); + if params.len() != statement.params().len() { + return Err(Error::parameters(params.len(), statement.params().len())); + } + + encode_bind_raw( statement.name(), - statement.params(), - params, + params.zip(statement.params().iter().cloned()), portal, buf, ) } -fn encode_bind_with_statement_name_and_param_types( +fn encode_bind_raw( statement_name: &str, - param_types: &[Type], params: I, portal: &str, buf: &mut BytesMut, ) -> Result<(), Error> where P: BorrowToSql, - I: IntoIterator, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { - let params = params.into_iter(); - - if param_types.len() != params.len() { - return Err(Error::parameters(params.len(), param_types.len())); - } - let (param_formats, params): (Vec<_>, Vec<_>) = params - .zip(param_types.iter()) - .map(|(p, ty)| (p.borrow_to_sql().encode_format(ty) as i16, p)) + .into_iter() + .map(|(p, ty)| (p.borrow_to_sql().encode_format(&ty) as i16, (p, ty))) .unzip(); - let params = params.into_iter(); - let mut error_idx = 0; let r = frontend::bind( portal, statement_name, param_formats, - params.zip(param_types).enumerate(), - |(idx, (param, ty)), buf| match param.borrow_to_sql().to_sql_checked(ty, buf) { + params.into_iter().enumerate(), + |(idx, (param, ty)), buf| match param.borrow_to_sql().to_sql_checked(&ty, buf) { Ok(IsNull::No) => Ok(postgres_protocol::IsNull::No), Ok(IsNull::Yes) => Ok(postgres_protocol::IsNull::Yes), Err(e) => { diff --git a/tokio-postgres/src/transaction.rs b/tokio-postgres/src/transaction.rs index 3e62b2ac7..17a50b60f 100644 --- a/tokio-postgres/src/transaction.rs +++ b/tokio-postgres/src/transaction.rs @@ -149,6 +149,24 @@ impl<'a> Transaction<'a> { self.client.query_raw(statement, params).await } + /// Like `Client::query_typed`. + pub async fn query_typed( + &self, + statement: &str, + params: &[(&(dyn ToSql + Sync), Type)], + ) -> Result, Error> { + self.client.query_typed(statement, params).await + } + + /// Like `Client::query_typed_raw`. + pub async fn query_typed_raw(&self, query: &str, params: I) -> Result + where + P: BorrowToSql, + I: IntoIterator, + { + self.client.query_typed_raw(query, params).await + } + /// Like `Client::execute`. pub async fn execute( &self, @@ -227,15 +245,6 @@ impl<'a> Transaction<'a> { query::query_portal(self.client.inner(), portal, max_rows).await } - /// Like `Client::query_typed`. - pub async fn query_typed( - &self, - statement: &str, - params: &[(&(dyn ToSql + Sync), Type)], - ) -> Result, Error> { - self.client.query_typed(statement, params).await - } - /// Like `Client::copy_in`. pub async fn copy_in(&self, statement: &T) -> Result, Error> where From a0b2d701ebee8fd5c5b3d6ee5cf0cde5d7f36a65 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 21 Jul 2024 20:04:35 -0400 Subject: [PATCH 143/207] Fix cancellation of TransactionBuilder::start --- tokio-postgres/src/client.rs | 42 ++--------------------- tokio-postgres/src/transaction_builder.rs | 40 +++++++++++++++++++-- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index b04f05f88..92eabde36 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -1,4 +1,4 @@ -use crate::codec::{BackendMessages, FrontendMessage}; +use crate::codec::BackendMessages; use crate::config::SslMode; use crate::connection::{Request, RequestMessages}; use crate::copy_out::CopyOutStream; @@ -21,7 +21,7 @@ use fallible_iterator::FallibleIterator; use futures_channel::mpsc; use futures_util::{future, pin_mut, ready, StreamExt, TryStreamExt}; use parking_lot::Mutex; -use postgres_protocol::message::{backend::Message, frontend}; +use postgres_protocol::message::backend::Message; use postgres_types::BorrowToSql; use std::collections::HashMap; use std::fmt; @@ -532,43 +532,7 @@ impl Client { /// /// The transaction will roll back by default - use the `commit` method to commit it. pub async fn transaction(&mut self) -> Result, Error> { - struct RollbackIfNotDone<'me> { - client: &'me Client, - done: bool, - } - - impl<'a> Drop for RollbackIfNotDone<'a> { - fn drop(&mut self) { - if self.done { - return; - } - - let buf = self.client.inner().with_buf(|buf| { - frontend::query("ROLLBACK", buf).unwrap(); - buf.split().freeze() - }); - let _ = self - .client - .inner() - .send(RequestMessages::Single(FrontendMessage::Raw(buf))); - } - } - - // This is done, as `Future` created by this method can be dropped after - // `RequestMessages` is synchronously send to the `Connection` by - // `batch_execute()`, but before `Responses` is asynchronously polled to - // completion. In that case `Transaction` won't be created and thus - // won't be rolled back. - { - let mut cleaner = RollbackIfNotDone { - client: self, - done: false, - }; - self.batch_execute("BEGIN").await?; - cleaner.done = true; - } - - Ok(Transaction::new(self)) + self.build_transaction().start().await } /// Returns a builder for a transaction with custom settings. diff --git a/tokio-postgres/src/transaction_builder.rs b/tokio-postgres/src/transaction_builder.rs index 9718ac588..93e9e9801 100644 --- a/tokio-postgres/src/transaction_builder.rs +++ b/tokio-postgres/src/transaction_builder.rs @@ -1,4 +1,6 @@ -use crate::{Client, Error, Transaction}; +use postgres_protocol::message::frontend; + +use crate::{codec::FrontendMessage, connection::RequestMessages, Client, Error, Transaction}; /// The isolation level of a database transaction. #[derive(Debug, Copy, Clone)] @@ -106,7 +108,41 @@ impl<'a> TransactionBuilder<'a> { query.push_str(s); } - self.client.batch_execute(&query).await?; + struct RollbackIfNotDone<'me> { + client: &'me Client, + done: bool, + } + + impl<'a> Drop for RollbackIfNotDone<'a> { + fn drop(&mut self) { + if self.done { + return; + } + + let buf = self.client.inner().with_buf(|buf| { + frontend::query("ROLLBACK", buf).unwrap(); + buf.split().freeze() + }); + let _ = self + .client + .inner() + .send(RequestMessages::Single(FrontendMessage::Raw(buf))); + } + } + + // This is done as `Future` created by this method can be dropped after + // `RequestMessages` is synchronously send to the `Connection` by + // `batch_execute()`, but before `Responses` is asynchronously polled to + // completion. In that case `Transaction` won't be created and thus + // won't be rolled back. + { + let mut cleaner = RollbackIfNotDone { + client: self.client, + done: false, + }; + self.client.batch_execute(&query).await?; + cleaner.done = true; + } Ok(Transaction::new(self.client)) } From c3580774fcdc4597dac81e1128ef8bef1e6ff3a7 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 21 Jul 2024 20:23:50 -0400 Subject: [PATCH 144/207] Release postgres-protocol v0.6.7 --- postgres-protocol/CHANGELOG.md | 17 ++++++++++++++++- postgres-protocol/Cargo.toml | 2 +- postgres-types/Cargo.toml | 2 +- tokio-postgres/Cargo.toml | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/postgres-protocol/CHANGELOG.md b/postgres-protocol/CHANGELOG.md index 1c371675c..54dce91b0 100644 --- a/postgres-protocol/CHANGELOG.md +++ b/postgres-protocol/CHANGELOG.md @@ -1,6 +1,21 @@ # Change Log -## v0.6.6 -2023-08-19 +## v0.6.7 - 2024-07-21 + +### Deprecated + +* Deprecated `ErrorField::value`. + +### Added + +* Added a `Clone` implementation for `DataRowBody`. +* Added `ErrorField::value_bytes`. + +### Changed + +* Upgraded `base64`. + +## v0.6.6 - 2023-08-19 ### Added diff --git a/postgres-protocol/Cargo.toml b/postgres-protocol/Cargo.toml index a8a130495..49cf2d59c 100644 --- a/postgres-protocol/Cargo.toml +++ b/postgres-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-protocol" -version = "0.6.6" +version = "0.6.7" authors = ["Steven Fackler "] edition = "2018" description = "Low level Postgres protocol APIs" diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index 33296db2c..984fd186f 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -31,7 +31,7 @@ with-time-0_3 = ["time-03"] [dependencies] bytes = "1.0" fallible-iterator = "0.2" -postgres-protocol = { version = "0.6.5", path = "../postgres-protocol" } +postgres-protocol = { version = "0.6.7", path = "../postgres-protocol" } postgres-derive = { version = "0.4.5", optional = true, path = "../postgres-derive" } array-init = { version = "2", optional = true } diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 2e080cfb2..92f4ee696 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -54,7 +54,7 @@ parking_lot = "0.12" percent-encoding = "2.0" pin-project-lite = "0.2" phf = "0.11" -postgres-protocol = { version = "0.6.6", path = "../postgres-protocol" } +postgres-protocol = { version = "0.6.7", path = "../postgres-protocol" } postgres-types = { version = "0.2.5", path = "../postgres-types" } tokio = { version = "1.27", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } From 6b4566b132ca4a81c06eaf35eb63318a69360f48 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 21 Jul 2024 20:28:22 -0400 Subject: [PATCH 145/207] Release postgres-types v0.2.7 --- postgres-types/CHANGELOG.md | 8 ++++++++ postgres-types/Cargo.toml | 2 +- tokio-postgres/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/postgres-types/CHANGELOG.md b/postgres-types/CHANGELOG.md index 157a2cc7d..1e5cd31d8 100644 --- a/postgres-types/CHANGELOG.md +++ b/postgres-types/CHANGELOG.md @@ -2,9 +2,17 @@ ## Unreleased +## v0.2.7 - 2024-07-21 + +### Added + +* Added `Default` implementation for `Json`. +* Added a `js` feature for WASM compatibility. + ### Changed * `FromStr` implementation for `PgLsn` no longer allocates a `Vec` when splitting an lsn string on it's `/`. +* The `eui48-1` feature no longer enables default features of the `eui48` library. ## v0.2.6 - 2023-08-19 diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index 984fd186f..e2d21b358 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres-types" -version = "0.2.6" +version = "0.2.7" authors = ["Steven Fackler "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index 92f4ee696..f762b1184 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -55,7 +55,7 @@ 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.5", path = "../postgres-types" } +postgres-types = { version = "0.2.7", path = "../postgres-types" } tokio = { version = "1.27", features = ["io-util"] } tokio-util = { version = "0.7", features = ["codec"] } rand = "0.8.5" From 92266188e8fd081be8e29d425b9fd334d2039196 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 21 Jul 2024 20:36:18 -0400 Subject: [PATCH 146/207] Release tokio-postgres v0.7.11 --- postgres-native-tls/Cargo.toml | 2 +- postgres-openssl/Cargo.toml | 2 +- postgres/Cargo.toml | 2 +- tokio-postgres/CHANGELOG.md | 24 +++++++++++++++++++++--- tokio-postgres/Cargo.toml | 2 +- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/postgres-native-tls/Cargo.toml b/postgres-native-tls/Cargo.toml index 936eeeaa4..6c17d0889 100644 --- a/postgres-native-tls/Cargo.toml +++ b/postgres-native-tls/Cargo.toml @@ -19,7 +19,7 @@ runtime = ["tokio-postgres/runtime"] native-tls = "0.2" tokio = "1.0" tokio-native-tls = "0.3" -tokio-postgres = { version = "0.7.0", path = "../tokio-postgres", default-features = false } +tokio-postgres = { version = "0.7.11", path = "../tokio-postgres", default-features = false } [dev-dependencies] futures-util = "0.3" diff --git a/postgres-openssl/Cargo.toml b/postgres-openssl/Cargo.toml index b7ebd3385..7c19070bf 100644 --- a/postgres-openssl/Cargo.toml +++ b/postgres-openssl/Cargo.toml @@ -19,7 +19,7 @@ runtime = ["tokio-postgres/runtime"] openssl = "0.10" tokio = "1.0" tokio-openssl = "0.6" -tokio-postgres = { version = "0.7.0", path = "../tokio-postgres", default-features = false } +tokio-postgres = { version = "0.7.11", path = "../tokio-postgres", default-features = false } [dev-dependencies] futures-util = "0.3" diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index 2ff3c875e..f1dc3c685 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -40,7 +40,7 @@ bytes = "1.0" fallible-iterator = "0.2" futures-util = { version = "0.3.14", features = ["sink"] } log = "0.4" -tokio-postgres = { version = "0.7.10", path = "../tokio-postgres" } +tokio-postgres = { version = "0.7.11", path = "../tokio-postgres" } tokio = { version = "1.0", features = ["rt", "time"] } [dev-dependencies] diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index 775c22e34..e0be26296 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -2,10 +2,28 @@ ## Unreleased +## v0.7.11 - 2024-07-21 + +### Fixed + +* Fixed handling of non-UTF8 error fields which can be sent after failed handshakes. +* Fixed cancellation handling of `TransactionBuilder::start` futures. + +### Added + +* Added `table_oid` and `field_id` fields to `Columns` struct of prepared statements. +* Added `GenericClient::simple_query`. +* Added `#[track_caller]` to `Row::get` and `SimpleQueryRow::get`. +* Added `TargetSessionAttrs::ReadOnly`. +* Added `Debug` implementation for `Statement`. +* Added `Clone` implementation for `Row`. +* Added `SimpleQueryMessage::RowDescription`. +* Added `{Client, Transaction, GenericClient}::query_typed`. + +### Changed + * Disable `rustc-serialize` compatibility of `eui48-1` dependency -* Remove tests for `eui48-04` -* Add `table_oid` and `field_id` fields to `Columns` struct of prepared statements. -* Add `GenericClient::simple_query`. +* Config setters now take `impl Into`. ## v0.7.10 - 2023-08-25 diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index f762b1184..c2f80dc7e 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tokio-postgres" -version = "0.7.10" +version = "0.7.11" authors = ["Steven Fackler "] edition = "2018" license = "MIT OR Apache-2.0" From 9f196e7f5ba6067efe55f758d743cdfd9b606cff Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 21 Jul 2024 20:38:52 -0400 Subject: [PATCH 147/207] Release postgres v0.19.8 --- postgres-native-tls/Cargo.toml | 2 +- postgres-openssl/Cargo.toml | 2 +- postgres/CHANGELOG.md | 6 ++++++ postgres/Cargo.toml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/postgres-native-tls/Cargo.toml b/postgres-native-tls/Cargo.toml index 6c17d0889..02259b3dc 100644 --- a/postgres-native-tls/Cargo.toml +++ b/postgres-native-tls/Cargo.toml @@ -24,4 +24,4 @@ tokio-postgres = { version = "0.7.11", path = "../tokio-postgres", default-featu [dev-dependencies] futures-util = "0.3" tokio = { version = "1.0", features = ["macros", "net", "rt"] } -postgres = { version = "0.19.0", path = "../postgres" } +postgres = { version = "0.19.8", path = "../postgres" } diff --git a/postgres-openssl/Cargo.toml b/postgres-openssl/Cargo.toml index 7c19070bf..9013384a2 100644 --- a/postgres-openssl/Cargo.toml +++ b/postgres-openssl/Cargo.toml @@ -24,4 +24,4 @@ tokio-postgres = { version = "0.7.11", path = "../tokio-postgres", default-featu [dev-dependencies] futures-util = "0.3" tokio = { version = "1.0", features = ["macros", "net", "rt"] } -postgres = { version = "0.19.0", path = "../postgres" } +postgres = { version = "0.19.8", path = "../postgres" } diff --git a/postgres/CHANGELOG.md b/postgres/CHANGELOG.md index 7f856b5ac..258cdb518 100644 --- a/postgres/CHANGELOG.md +++ b/postgres/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## v0.19.8 - 2024-07-21 + +### Added + +* Added `{Client, Transaction, GenericClient}::query_typed`. + ## v0.19.7 - 2023-08-25 ## Fixed diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index f1dc3c685..ff95c4f14 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "postgres" -version = "0.19.7" +version = "0.19.8" authors = ["Steven Fackler "] edition = "2018" license = "MIT OR Apache-2.0" From 6de0fceebe3c1800a5e812f35449e464fbc33f55 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Mon, 22 Jul 2024 15:54:40 -0400 Subject: [PATCH 148/207] Add jiff support --- postgres-types/CHANGELOG.md | 4 ++ postgres-types/Cargo.toml | 2 + postgres-types/src/jiff_01.rs | 118 ++++++++++++++++++++++++++++++++++ postgres-types/src/lib.rs | 7 ++ postgres/CHANGELOG.md | 6 ++ postgres/Cargo.toml | 1 + tokio-postgres/CHANGELOG.md | 4 ++ tokio-postgres/Cargo.toml | 2 + tokio-postgres/src/lib.rs | 1 + 9 files changed, 145 insertions(+) create mode 100644 postgres-types/src/jiff_01.rs diff --git a/postgres-types/CHANGELOG.md b/postgres-types/CHANGELOG.md index 1e5cd31d8..b11e18d32 100644 --- a/postgres-types/CHANGELOG.md +++ b/postgres-types/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Added + +* Added support for `jiff` 0.1 via the `with-jiff-01` feature. + ## v0.2.7 - 2024-07-21 ### Added diff --git a/postgres-types/Cargo.toml b/postgres-types/Cargo.toml index e2d21b358..941f4fcc4 100644 --- a/postgres-types/Cargo.toml +++ b/postgres-types/Cargo.toml @@ -21,6 +21,7 @@ with-eui48-0_4 = ["eui48-04"] with-eui48-1 = ["eui48-1"] with-geo-types-0_6 = ["geo-types-06"] with-geo-types-0_7 = ["geo-types-0_7"] +with-jiff-0_1 = ["jiff-01"] with-serde_json-1 = ["serde-1", "serde_json-1"] with-smol_str-01 = ["smol_str-01"] with-uuid-0_8 = ["uuid-08"] @@ -46,6 +47,7 @@ eui48-04 = { version = "0.4", package = "eui48", optional = true } eui48-1 = { version = "1.0", package = "eui48", optional = true, default-features = false } geo-types-06 = { version = "0.6", package = "geo-types", optional = true } geo-types-0_7 = { version = "0.7", package = "geo-types", optional = true } +jiff-01 = { version = "0.1", package = "jiff", optional = true } serde-1 = { version = "1.0", package = "serde", optional = true } serde_json-1 = { version = "1.0", package = "serde_json", optional = true } uuid-08 = { version = "0.8", package = "uuid", optional = true } diff --git a/postgres-types/src/jiff_01.rs b/postgres-types/src/jiff_01.rs new file mode 100644 index 000000000..eec6aa2f8 --- /dev/null +++ b/postgres-types/src/jiff_01.rs @@ -0,0 +1,118 @@ +use bytes::BytesMut; +use jiff_01::{ + civil::{Date, DateTime, Time}, + tz::TimeZone, + Span, Timestamp as JiffTimestamp, Zoned, +}; +use postgres_protocol::types; +use std::error::Error; + +use crate::{FromSql, IsNull, ToSql, Type}; + +const fn base() -> DateTime { + DateTime::constant(2000, 1, 1, 0, 0, 0, 0) +} + +impl<'a> FromSql<'a> for DateTime { + fn from_sql(_: &Type, raw: &[u8]) -> Result> { + let t = types::timestamp_from_sql(raw)?; + Ok(base().checked_add(Span::new().microseconds(t))?) + } + + accepts!(TIMESTAMP); +} + +impl ToSql for DateTime { + fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { + types::timestamp_to_sql(self.since(base())?.get_microseconds(), w); + Ok(IsNull::No) + } + + accepts!(TIMESTAMP); + to_sql_checked!(); +} + +impl<'a> FromSql<'a> for JiffTimestamp { + fn from_sql(type_: &Type, raw: &[u8]) -> Result> { + Ok(DateTime::from_sql(type_, raw)? + .to_zoned(TimeZone::UTC)? + .timestamp()) + } + + accepts!(TIMESTAMPTZ); +} + +impl ToSql for JiffTimestamp { + fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { + types::timestamp_to_sql( + self.since(base().to_zoned(TimeZone::UTC)?)? + .get_microseconds(), + w, + ); + Ok(IsNull::No) + } + + accepts!(TIMESTAMPTZ); + to_sql_checked!(); +} + +impl<'a> FromSql<'a> for Zoned { + fn from_sql(type_: &Type, raw: &[u8]) -> Result> { + Ok(JiffTimestamp::from_sql(type_, raw)?.to_zoned(TimeZone::UTC)) + } + + accepts!(TIMESTAMPTZ); +} + +impl ToSql for Zoned { + fn to_sql( + &self, + type_: &Type, + w: &mut BytesMut, + ) -> Result> { + self.timestamp().to_sql(type_, w) + } + + accepts!(TIMESTAMPTZ); + to_sql_checked!(); +} + +impl<'a> FromSql<'a> for Date { + fn from_sql(_: &Type, raw: &[u8]) -> Result> { + let jd = types::date_from_sql(raw)?; + Ok(base().date().checked_add(Span::new().days(jd))?) + } + + accepts!(DATE); +} + +impl ToSql for Date { + fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { + let jd = self.since(base().date())?.get_days(); + types::date_to_sql(jd, w); + Ok(IsNull::No) + } + + accepts!(DATE); + to_sql_checked!(); +} + +impl<'a> FromSql<'a> for Time { + fn from_sql(_: &Type, raw: &[u8]) -> Result> { + let usec = types::time_from_sql(raw)?; + Ok(Time::midnight() + Span::new().microseconds(usec)) + } + + accepts!(TIME); +} + +impl ToSql for Time { + fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { + let delta = self.since(Time::midnight())?; + types::time_to_sql(delta.get_microseconds(), w); + Ok(IsNull::No) + } + + accepts!(TIME); + to_sql_checked!(); +} diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index 492039766..7d6d976c6 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -276,6 +276,8 @@ mod eui48_1; mod geo_types_06; #[cfg(feature = "with-geo-types-0_7")] mod geo_types_07; +#[cfg(feature = "with-jiff-0_1")] +mod jiff_01; #[cfg(feature = "with-serde_json-1")] mod serde_json_1; #[cfg(feature = "with-smol_str-01")] @@ -491,6 +493,11 @@ impl WrongType { /// | `time::OffsetDateTime` | TIMESTAMP WITH TIME ZONE | /// | `time::Date` | DATE | /// | `time::Time` | TIME | +/// | `jiff::civil::DateTime` | TIMESTAMP | +/// | `jiff::Timestamp` | TIMESTAMP WITH TIME ZONE | +/// | `jiff::Zoned` | TIMESTAMP WITH TIME ZONE | +/// | `jiff::civil::Date` | DATE | +/// | `jiff::civil::Time` | TIME | /// | `eui48::MacAddress` | MACADDR | /// | `geo_types::Point` | POINT | /// | `geo_types::Rect` | BOX | diff --git a/postgres/CHANGELOG.md b/postgres/CHANGELOG.md index 258cdb518..6feb629e4 100644 --- a/postgres/CHANGELOG.md +++ b/postgres/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## Unreleased + +### Added + +* Added support for `jiff` 0.1 via the `with-jiff-01` feature. + ## v0.19.8 - 2024-07-21 ### Added diff --git a/postgres/Cargo.toml b/postgres/Cargo.toml index ff95c4f14..e0e580f7d 100644 --- a/postgres/Cargo.toml +++ b/postgres/Cargo.toml @@ -28,6 +28,7 @@ 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"] with-geo-types-0_7 = ["tokio-postgres/with-geo-types-0_7"] +with-jiff-0_1 = ["tokio-postgres/with-jiff-0_1"] with-serde_json-1 = ["tokio-postgres/with-serde_json-1"] with-smol_str-01 = ["tokio-postgres/with-smol_str-01"] with-uuid-0_8 = ["tokio-postgres/with-uuid-0_8"] diff --git a/tokio-postgres/CHANGELOG.md b/tokio-postgres/CHANGELOG.md index e0be26296..bf17ec486 100644 --- a/tokio-postgres/CHANGELOG.md +++ b/tokio-postgres/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Added + +* Added support for `jiff` 0.1 via the `with-jiff-01` feature. + ## v0.7.11 - 2024-07-21 ### Fixed diff --git a/tokio-postgres/Cargo.toml b/tokio-postgres/Cargo.toml index c2f80dc7e..e1e84f7b1 100644 --- a/tokio-postgres/Cargo.toml +++ b/tokio-postgres/Cargo.toml @@ -34,6 +34,7 @@ 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"] with-geo-types-0_7 = ["postgres-types/with-geo-types-0_7"] +with-jiff-0_1 = ["postgres-types/with-jiff-0_1"] with-serde_json-1 = ["postgres-types/with-serde_json-1"] with-smol_str-01 = ["postgres-types/with-smol_str-01"] with-uuid-0_8 = ["postgres-types/with-uuid-0_8"] @@ -81,6 +82,7 @@ chrono-04 = { version = "0.4", package = "chrono", default-features = false } eui48-1 = { version = "1.0", package = "eui48", default-features = false } geo-types-06 = { version = "0.6", package = "geo-types" } geo-types-07 = { version = "0.7", package = "geo-types" } +jiff-01 = { version = "0.1", package = "jiff" } serde-1 = { version = "1.0", package = "serde" } serde_json-1 = { version = "1.0", package = "serde_json" } smol_str-01 = { version = "0.1", package = "smol_str" } diff --git a/tokio-postgres/src/lib.rs b/tokio-postgres/src/lib.rs index a603158fb..ec843d511 100644 --- a/tokio-postgres/src/lib.rs +++ b/tokio-postgres/src/lib.rs @@ -111,6 +111,7 @@ //! | `with-eui48-1` | Enable support for the 1.0 version of the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 1.0 | no | //! | `with-geo-types-0_6` | Enable support for the 0.6 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.6.0) 0.6 | no | //! | `with-geo-types-0_7` | Enable support for the 0.7 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.7.0) 0.7 | no | +//! | `with-jiff-0_1` | Enable support for the 0.1 version of the `jiff` crate. | [jiff](https://crates.io/crates/jiff/0.1.0) 0.1 | no | //! | `with-serde_json-1` | Enable support for the `serde_json` crate. | [serde_json](https://crates.io/crates/serde_json) 1.0 | no | //! | `with-uuid-0_8` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 0.8 | no | //! | `with-uuid-1` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 1.0 | no | From 0fc4005ed31e3705a04cb7e58eb220d89b922dd0 Mon Sep 17 00:00:00 2001 From: Ramnivas Laddad Date: Mon, 22 Jul 2024 15:07:44 -0700 Subject: [PATCH 149/207] For `query_typed`, deal with the no-data case. If a query returns no data, we receive `Message::NoData`, which signals the completion of the query. However, we treated it as a no-op, leading to processing other messages and eventual failure. This PR fixes the issue and updates the `query_typed` tests to cover this scenario. --- tokio-postgres/src/query.rs | 13 +++++++++---- tokio-postgres/tests/test/main.rs | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index be42d66b6..3ab002871 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -89,10 +89,15 @@ where loop { match responses.next().await? { - Message::ParseComplete - | Message::BindComplete - | Message::ParameterDescription(_) - | Message::NoData => {} + Message::ParseComplete | Message::BindComplete | Message::ParameterDescription(_) => {} + Message::NoData => { + return Ok(RowStream { + statement: Statement::unnamed(vec![], vec![]), + responses, + rows_affected: None, + _p: PhantomPinned, + }); + } Message::RowDescription(row_description) => { let mut columns: Vec = vec![]; let mut it = row_description.fields(); diff --git a/tokio-postgres/tests/test/main.rs b/tokio-postgres/tests/test/main.rs index 84c46d101..9a6aa26fe 100644 --- a/tokio-postgres/tests/test/main.rs +++ b/tokio-postgres/tests/test/main.rs @@ -997,6 +997,13 @@ async fn query_typed_no_transaction() { assert_eq!(second_row.get::<_, i32>(1), 40); assert_eq!(second_row.get::<_, &str>(2), "literal"); assert_eq!(second_row.get::<_, i32>(3), 5); + + // Test for UPDATE that returns no data + let updated_rows = client + .query_typed("UPDATE foo set age = 33", &[]) + .await + .unwrap(); + assert_eq!(updated_rows.len(), 0); } #[tokio::test] @@ -1064,4 +1071,11 @@ async fn query_typed_with_transaction() { assert_eq!(second_row.get::<_, i32>(1), 40); assert_eq!(second_row.get::<_, &str>(2), "literal"); assert_eq!(second_row.get::<_, i32>(3), 5); + + // Test for UPDATE that returns no data + let updated_rows = transaction + .query_typed("UPDATE foo set age = 33", &[]) + .await + .unwrap(); + assert_eq!(updated_rows.len(), 0); } From aa10f0d75cb23757c9a87fe58363e4e26ae19d1e Mon Sep 17 00:00:00 2001 From: Qiu Chaofan Date: Tue, 23 Jul 2024 13:36:51 +0800 Subject: [PATCH 150/207] Support AIX keepalive --- tokio-postgres/src/keepalive.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tokio-postgres/src/keepalive.rs b/tokio-postgres/src/keepalive.rs index c409eb0ea..7bdd76341 100644 --- a/tokio-postgres/src/keepalive.rs +++ b/tokio-postgres/src/keepalive.rs @@ -12,12 +12,18 @@ impl From<&KeepaliveConfig> for TcpKeepalive { fn from(keepalive_config: &KeepaliveConfig) -> Self { let mut tcp_keepalive = Self::new().with_time(keepalive_config.idle); - #[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "openbsd")))] + #[cfg(not(any( + target_os = "aix", + target_os = "redox", + target_os = "solaris", + target_os = "openbsd" + )))] if let Some(interval) = keepalive_config.interval { tcp_keepalive = tcp_keepalive.with_interval(interval); } #[cfg(not(any( + target_os = "aix", target_os = "redox", target_os = "solaris", target_os = "windows", From df2f37d848f5779ed1dc6c1a8f8ded32a15e70c3 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 23 Jul 2024 07:54:19 -0400 Subject: [PATCH 151/207] Remove unecessary alias for Timestamp --- postgres-types/src/jiff_01.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/postgres-types/src/jiff_01.rs b/postgres-types/src/jiff_01.rs index eec6aa2f8..c2e4ef06e 100644 --- a/postgres-types/src/jiff_01.rs +++ b/postgres-types/src/jiff_01.rs @@ -2,7 +2,7 @@ use bytes::BytesMut; use jiff_01::{ civil::{Date, DateTime, Time}, tz::TimeZone, - Span, Timestamp as JiffTimestamp, Zoned, + Span, Timestamp, Zoned, }; use postgres_protocol::types; use std::error::Error; @@ -32,8 +32,8 @@ impl ToSql for DateTime { to_sql_checked!(); } -impl<'a> FromSql<'a> for JiffTimestamp { - fn from_sql(type_: &Type, raw: &[u8]) -> Result> { +impl<'a> FromSql<'a> for Timestamp { + fn from_sql(type_: &Type, raw: &[u8]) -> Result> { Ok(DateTime::from_sql(type_, raw)? .to_zoned(TimeZone::UTC)? .timestamp()) @@ -42,7 +42,7 @@ impl<'a> FromSql<'a> for JiffTimestamp { accepts!(TIMESTAMPTZ); } -impl ToSql for JiffTimestamp { +impl ToSql for Timestamp { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { types::timestamp_to_sql( self.since(base().to_zoned(TimeZone::UTC)?)? @@ -58,7 +58,7 @@ impl ToSql for JiffTimestamp { impl<'a> FromSql<'a> for Zoned { fn from_sql(type_: &Type, raw: &[u8]) -> Result> { - Ok(JiffTimestamp::from_sql(type_, raw)?.to_zoned(TimeZone::UTC)) + Ok(Timestamp::from_sql(type_, raw)?.to_zoned(TimeZone::UTC)) } accepts!(TIMESTAMPTZ); From f00d208959c8c76c9bbe943f53a9a261ef1d2315 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 23 Jul 2024 07:56:00 -0400 Subject: [PATCH 152/207] Update impl for Timestamp The impl now directly computes `Timestamp` rather than going through `DateTime` and `Zoned`. --- postgres-types/src/jiff_01.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/postgres-types/src/jiff_01.rs b/postgres-types/src/jiff_01.rs index c2e4ef06e..d8f8723b6 100644 --- a/postgres-types/src/jiff_01.rs +++ b/postgres-types/src/jiff_01.rs @@ -13,6 +13,13 @@ const fn base() -> DateTime { DateTime::constant(2000, 1, 1, 0, 0, 0, 0) } +/// The number of seconds from 2000-01-01 00:00:00 UTC to the Unix epoch. +const Y2K_EPOCH: i64 = 946684800; + +fn base_ts() -> Timestamp { + Timestamp::new(Y2K_EPOCH, 0).unwrap() +} + impl<'a> FromSql<'a> for DateTime { fn from_sql(_: &Type, raw: &[u8]) -> Result> { let t = types::timestamp_from_sql(raw)?; @@ -33,10 +40,9 @@ impl ToSql for DateTime { } impl<'a> FromSql<'a> for Timestamp { - fn from_sql(type_: &Type, raw: &[u8]) -> Result> { - Ok(DateTime::from_sql(type_, raw)? - .to_zoned(TimeZone::UTC)? - .timestamp()) + fn from_sql(_: &Type, raw: &[u8]) -> Result> { + let t = types::timestamp_from_sql(raw)?; + Ok(base_ts().checked_add(Span::new().microseconds(t))?) } accepts!(TIMESTAMPTZ); @@ -44,11 +50,7 @@ impl<'a> FromSql<'a> for Timestamp { impl ToSql for Timestamp { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - types::timestamp_to_sql( - self.since(base().to_zoned(TimeZone::UTC)?)? - .get_microseconds(), - w, - ); + types::timestamp_to_sql(self.since(base_ts())?.get_microseconds(), w); Ok(IsNull::No) } From 815a5d3ae9a580dcc6db3312c8945417eac680f2 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 23 Jul 2024 07:58:47 -0400 Subject: [PATCH 153/207] Remove impl for `Zoned` `Timestamp` already has impl and is semantically accurate for mapping to `timestamptz`, unlike `Zoned`. End users can do their own conversions from `Timestamp` to `Zoned` if desired. --- postgres-types/src/jiff_01.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/postgres-types/src/jiff_01.rs b/postgres-types/src/jiff_01.rs index d8f8723b6..8a0a38f7c 100644 --- a/postgres-types/src/jiff_01.rs +++ b/postgres-types/src/jiff_01.rs @@ -58,27 +58,6 @@ impl ToSql for Timestamp { to_sql_checked!(); } -impl<'a> FromSql<'a> for Zoned { - fn from_sql(type_: &Type, raw: &[u8]) -> Result> { - Ok(Timestamp::from_sql(type_, raw)?.to_zoned(TimeZone::UTC)) - } - - accepts!(TIMESTAMPTZ); -} - -impl ToSql for Zoned { - fn to_sql( - &self, - type_: &Type, - w: &mut BytesMut, - ) -> Result> { - self.timestamp().to_sql(type_, w) - } - - accepts!(TIMESTAMPTZ); - to_sql_checked!(); -} - impl<'a> FromSql<'a> for Date { fn from_sql(_: &Type, raw: &[u8]) -> Result> { let jd = types::date_from_sql(raw)?; From e19b3dc164ba0cc4f1e601149dc7e7b2837e7276 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Wed, 14 Aug 2024 09:00:42 -0400 Subject: [PATCH 154/207] Rename PG_EPOCH --- postgres-types/src/jiff_01.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/postgres-types/src/jiff_01.rs b/postgres-types/src/jiff_01.rs index 8a0a38f7c..871d35f62 100644 --- a/postgres-types/src/jiff_01.rs +++ b/postgres-types/src/jiff_01.rs @@ -1,8 +1,7 @@ use bytes::BytesMut; use jiff_01::{ civil::{Date, DateTime, Time}, - tz::TimeZone, - Span, Timestamp, Zoned, + Span, Timestamp, }; use postgres_protocol::types; use std::error::Error; @@ -13,11 +12,11 @@ const fn base() -> DateTime { DateTime::constant(2000, 1, 1, 0, 0, 0, 0) } -/// The number of seconds from 2000-01-01 00:00:00 UTC to the Unix epoch. -const Y2K_EPOCH: i64 = 946684800; +/// The number of seconds from the Unix epoch to 2000-01-01 00:00:00 UTC. +const PG_EPOCH: i64 = 946684800; fn base_ts() -> Timestamp { - Timestamp::new(Y2K_EPOCH, 0).unwrap() + Timestamp::new(PG_EPOCH, 0).unwrap() } impl<'a> FromSql<'a> for DateTime { From c96342d7f6e1b86db752e96482ad372024062fab Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Wed, 14 Aug 2024 09:14:41 -0400 Subject: [PATCH 155/207] Fix ToSql This sets the smallest unit to microseconds when calculating time deltas. Previously, the number of microseconds was expressed improperly because the rounding was not set. --- postgres-types/src/jiff_01.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/postgres-types/src/jiff_01.rs b/postgres-types/src/jiff_01.rs index 871d35f62..54768c10d 100644 --- a/postgres-types/src/jiff_01.rs +++ b/postgres-types/src/jiff_01.rs @@ -1,7 +1,7 @@ use bytes::BytesMut; use jiff_01::{ civil::{Date, DateTime, Time}, - Span, Timestamp, + Span, SpanRound, Timestamp, Unit, }; use postgres_protocol::types; use std::error::Error; @@ -12,13 +12,17 @@ const fn base() -> DateTime { DateTime::constant(2000, 1, 1, 0, 0, 0, 0) } -/// The number of seconds from the Unix epoch to 2000-01-01 00:00:00 UTC. +/// The number of seconds from the Unix epoch to 2000-01-01 00:00:00 UTC. const PG_EPOCH: i64 = 946684800; fn base_ts() -> Timestamp { Timestamp::new(PG_EPOCH, 0).unwrap() } +fn round_us<'a>() -> SpanRound<'a> { + SpanRound::new().largest(Unit::Microsecond) +} + impl<'a> FromSql<'a> for DateTime { fn from_sql(_: &Type, raw: &[u8]) -> Result> { let t = types::timestamp_from_sql(raw)?; @@ -30,7 +34,8 @@ impl<'a> FromSql<'a> for DateTime { impl ToSql for DateTime { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - types::timestamp_to_sql(self.since(base())?.get_microseconds(), w); + let span = self.since(base())?.round(round_us())?; + types::timestamp_to_sql(span.get_microseconds(), w); Ok(IsNull::No) } @@ -49,7 +54,8 @@ impl<'a> FromSql<'a> for Timestamp { impl ToSql for Timestamp { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - types::timestamp_to_sql(self.since(base_ts())?.get_microseconds(), w); + let span = self.since(base_ts())?.round(round_us())?; + types::timestamp_to_sql(span.get_microseconds(), w); Ok(IsNull::No) } @@ -88,8 +94,8 @@ impl<'a> FromSql<'a> for Time { impl ToSql for Time { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - let delta = self.since(Time::midnight())?; - types::time_to_sql(delta.get_microseconds(), w); + let span = self.since(Time::midnight())?.round(round_us())?; + types::time_to_sql(span.get_microseconds(), w); Ok(IsNull::No) } From afef88efb6a2555cf0bed88bd93d5e48f42bffe9 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Wed, 14 Aug 2024 20:39:55 -0400 Subject: [PATCH 156/207] Add jiff tests and overflow checks This adds tests in the same fashion as the existing ones for `chrono` and `time`. Overflow is now handled using fallible operations. For example, `Span:microseconds` is replaced with `Span::try_microseconds`. Postgres infinity values are workiing as expected. All tests are passing. --- postgres-types/src/jiff_01.rs | 71 +++++++-- tokio-postgres/tests/test/types/jiff_01.rs | 175 +++++++++++++++++++++ tokio-postgres/tests/test/types/mod.rs | 2 + 3 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 tokio-postgres/tests/test/types/jiff_01.rs diff --git a/postgres-types/src/jiff_01.rs b/postgres-types/src/jiff_01.rs index 54768c10d..d3215c0e6 100644 --- a/postgres-types/src/jiff_01.rs +++ b/postgres-types/src/jiff_01.rs @@ -23,10 +23,27 @@ fn round_us<'a>() -> SpanRound<'a> { SpanRound::new().largest(Unit::Microsecond) } +fn decode_err(_e: E) -> Box +where + E: Error, +{ + "value too large to decode".into() +} + +fn transmit_err(_e: E) -> Box +where + E: Error, +{ + "value too large to transmit".into() +} + impl<'a> FromSql<'a> for DateTime { fn from_sql(_: &Type, raw: &[u8]) -> Result> { - let t = types::timestamp_from_sql(raw)?; - Ok(base().checked_add(Span::new().microseconds(t))?) + let v = types::timestamp_from_sql(raw)?; + Span::new() + .try_microseconds(v) + .and_then(|s| base().checked_add(s)) + .map_err(decode_err) } accepts!(TIMESTAMP); @@ -34,8 +51,12 @@ impl<'a> FromSql<'a> for DateTime { impl ToSql for DateTime { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - let span = self.since(base())?.round(round_us())?; - types::timestamp_to_sql(span.get_microseconds(), w); + let v = self + .since(base()) + .and_then(|s| s.round(round_us())) + .map_err(transmit_err)? + .get_microseconds(); + types::timestamp_to_sql(v, w); Ok(IsNull::No) } @@ -45,8 +66,11 @@ impl ToSql for DateTime { impl<'a> FromSql<'a> for Timestamp { fn from_sql(_: &Type, raw: &[u8]) -> Result> { - let t = types::timestamp_from_sql(raw)?; - Ok(base_ts().checked_add(Span::new().microseconds(t))?) + let v = types::timestamp_from_sql(raw)?; + Span::new() + .try_microseconds(v) + .and_then(|s| base_ts().checked_add(s)) + .map_err(decode_err) } accepts!(TIMESTAMPTZ); @@ -54,8 +78,12 @@ impl<'a> FromSql<'a> for Timestamp { impl ToSql for Timestamp { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - let span = self.since(base_ts())?.round(round_us())?; - types::timestamp_to_sql(span.get_microseconds(), w); + let v = self + .since(base_ts()) + .and_then(|s| s.round(round_us())) + .map_err(transmit_err)? + .get_microseconds(); + types::timestamp_to_sql(v, w); Ok(IsNull::No) } @@ -65,17 +93,19 @@ impl ToSql for Timestamp { impl<'a> FromSql<'a> for Date { fn from_sql(_: &Type, raw: &[u8]) -> Result> { - let jd = types::date_from_sql(raw)?; - Ok(base().date().checked_add(Span::new().days(jd))?) + let v = types::date_from_sql(raw)?; + Span::new() + .try_days(v) + .and_then(|s| base().date().checked_add(s)) + .map_err(decode_err) } - accepts!(DATE); } impl ToSql for Date { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - let jd = self.since(base().date())?.get_days(); - types::date_to_sql(jd, w); + let v = self.since(base().date()).map_err(transmit_err)?.get_days(); + types::date_to_sql(v, w); Ok(IsNull::No) } @@ -85,8 +115,11 @@ impl ToSql for Date { impl<'a> FromSql<'a> for Time { fn from_sql(_: &Type, raw: &[u8]) -> Result> { - let usec = types::time_from_sql(raw)?; - Ok(Time::midnight() + Span::new().microseconds(usec)) + let v = types::time_from_sql(raw)?; + Span::new() + .try_microseconds(v) + .and_then(|s| Time::midnight().checked_add(s)) + .map_err(decode_err) } accepts!(TIME); @@ -94,8 +127,12 @@ impl<'a> FromSql<'a> for Time { impl ToSql for Time { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - let span = self.since(Time::midnight())?.round(round_us())?; - types::time_to_sql(span.get_microseconds(), w); + let v = self + .since(Time::midnight()) + .and_then(|s| s.round(round_us())) + .map_err(transmit_err)? + .get_microseconds(); + types::time_to_sql(v, w); Ok(IsNull::No) } diff --git a/tokio-postgres/tests/test/types/jiff_01.rs b/tokio-postgres/tests/test/types/jiff_01.rs new file mode 100644 index 000000000..7c9052676 --- /dev/null +++ b/tokio-postgres/tests/test/types/jiff_01.rs @@ -0,0 +1,175 @@ +use jiff_01::{ + civil::{Date as JiffDate, DateTime, Time}, + Timestamp as JiffTimestamp, +}; +use std::fmt; +use tokio_postgres::{ + types::{Date, FromSqlOwned, Timestamp}, + Client, +}; + +use crate::connect; +use crate::types::test_type; + +#[tokio::test] +async fn test_datetime_params() { + fn make_check(s: &str) -> (Option, &str) { + (Some(s.trim_matches('\'').parse().unwrap()), s) + } + test_type( + "TIMESTAMP", + &[ + make_check("'1970-01-01 00:00:00.010000000'"), + make_check("'1965-09-25 11:19:33.100314000'"), + make_check("'2010-02-09 23:11:45.120200000'"), + (None, "NULL"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_with_special_datetime_params() { + fn make_check(s: &str) -> (Timestamp, &str) { + (Timestamp::Value(s.trim_matches('\'').parse().unwrap()), s) + } + test_type( + "TIMESTAMP", + &[ + make_check("'1970-01-01 00:00:00.010000000'"), + make_check("'1965-09-25 11:19:33.100314000'"), + make_check("'2010-02-09 23:11:45.120200000'"), + (Timestamp::PosInfinity, "'infinity'"), + (Timestamp::NegInfinity, "'-infinity'"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_timestamp_params() { + fn make_check(s: &str) -> (Option, &str) { + (Some(s.trim_matches('\'').parse().unwrap()), s) + } + test_type( + "TIMESTAMP WITH TIME ZONE", + &[ + make_check("'1970-01-01 00:00:00.010000000Z'"), + make_check("'1965-09-25 11:19:33.100314000Z'"), + make_check("'2010-02-09 23:11:45.120200000Z'"), + (None, "NULL"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_with_special_timestamp_params() { + fn make_check(s: &str) -> (Timestamp, &str) { + (Timestamp::Value(s.trim_matches('\'').parse().unwrap()), s) + } + test_type( + "TIMESTAMP WITH TIME ZONE", + &[ + make_check("'1970-01-01 00:00:00.010000000Z'"), + make_check("'1965-09-25 11:19:33.100314000Z'"), + make_check("'2010-02-09 23:11:45.120200000Z'"), + (Timestamp::PosInfinity, "'infinity'"), + (Timestamp::NegInfinity, "'-infinity'"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_date_params() { + fn make_check(s: &str) -> (Option, &str) { + (Some(s.trim_matches('\'').parse().unwrap()), s) + } + test_type( + "DATE", + &[ + make_check("'1970-01-01'"), + make_check("'1965-09-25'"), + make_check("'2010-02-09'"), + (None, "NULL"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_with_special_date_params() { + fn make_check(s: &str) -> (Date, &str) { + (Date::Value(s.trim_matches('\'').parse().unwrap()), s) + } + test_type( + "DATE", + &[ + make_check("'1970-01-01'"), + make_check("'1965-09-25'"), + make_check("'2010-02-09'"), + (Date::PosInfinity, "'infinity'"), + (Date::NegInfinity, "'-infinity'"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_time_params() { + fn make_check(s: &str) -> (Option