diff --git a/postgres-types/src/jiff_01.rs b/postgres-types/src/jiff_01.rs index 8a0a38f7c..9566694d6 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, Unit, }; use postgres_protocol::types; use std::error::Error; @@ -23,7 +22,7 @@ fn base_ts() -> Timestamp { 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))?) + Ok(base().checked_add(Span::new().try_microseconds(t)?)?) } accepts!(TIMESTAMP); @@ -31,7 +30,7 @@ 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); + types::timestamp_to_sql(self.since(base())?.total(Unit::Microsecond)? as i64, w); Ok(IsNull::No) } @@ -42,7 +41,7 @@ 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))?) + Ok(base_ts().checked_add(Span::new().try_microseconds(t)?)?) } accepts!(TIMESTAMPTZ); @@ -50,7 +49,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_ts())?.get_microseconds(), w); + types::timestamp_to_sql(self.since(base_ts())?.total(Unit::Microsecond)? as i64, w); Ok(IsNull::No) } @@ -61,7 +60,7 @@ 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))?) + Ok(base().date().checked_add(Span::new().try_days(jd)?)?) } accepts!(DATE); @@ -69,7 +68,7 @@ impl<'a> FromSql<'a> for Date { impl ToSql for Date { fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result> { - let jd = self.since(base().date())?.get_days(); + let jd = self.since(base())?.total(Unit::Day)? as i32; types::date_to_sql(jd, w); Ok(IsNull::No) } @@ -81,7 +80,7 @@ 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)) + Ok(Time::midnight() + Span::new().try_microseconds(usec)?) } accepts!(TIME); @@ -90,7 +89,7 @@ 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); + types::time_to_sql(delta.total(Unit::Microsecond)? as i64, w); Ok(IsNull::No) } 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", 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); } 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..524f47010 --- /dev/null +++ b/tokio-postgres/tests/test/types/jiff_01.rs @@ -0,0 +1,176 @@ +use jiff_01::{civil::Date, civil::DateTime, civil::Time, Timestamp}; +use std::fmt; +use tokio_postgres::types::{self, FromSqlOwned}; +use tokio_postgres::Client; + +use crate::connect; +use crate::types::test_type; + +#[tokio::test] +async fn test_civil_datetime_params() { + fn make_check(time: &str) -> (Option, &str) { + ( + Some(DateTime::strptime("'%Y-%m-%d %H:%M:%S.%f'", time).unwrap()), + time, + ) + } + 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_civil_datetime_params() { + fn make_check(time: &str) -> (types::Timestamp, &str) { + ( + types::Timestamp::Value(DateTime::strptime("'%Y-%m-%d %H:%M:%S.%f'", time).unwrap()), + time, + ) + } + 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'"), + (types::Timestamp::PosInfinity, "'infinity'"), + (types::Timestamp::NegInfinity, "'-infinity'"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_timestamp_params() { + fn make_check(time: &str) -> (Option, &str) { + ( + Some(Timestamp::strptime("'%Y-%m-%d %H:%M:%S.%f %z'", time).unwrap()), + time, + ) + } + test_type( + "TIMESTAMP WITH TIME ZONE", + &[ + make_check("'1970-01-01 00:00:00.010000000 +0000'"), + make_check("'1965-09-25 11:19:33.100314000 +0000'"), + make_check("'2010-02-09 23:11:45.120200000 +0000'"), + make_check("'2010-11-20 17:11:45.120200000 +0500'"), + (None, "NULL"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_with_special_timestamp_params() { + fn make_check(time: &str) -> (types::Timestamp, &str) { + ( + types::Timestamp::Value( + Timestamp::strptime("'%Y-%m-%d %H:%M:%S.%f %z'", time).unwrap(), + ), + time, + ) + } + test_type( + "TIMESTAMP WITH TIME ZONE", + &[ + make_check("'1970-01-01 00:00:00.010000000 +0000'"), + make_check("'1965-09-25 11:19:33.100314000 +0000'"), + make_check("'2010-02-09 23:11:45.120200000 +0000'"), + (types::Timestamp::PosInfinity, "'infinity'"), + (types::Timestamp::NegInfinity, "'-infinity'"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_date_params() { + fn make_check(time: &str) -> (Option, &str) { + (Some(Date::strptime("'%Y-%m-%d'", time).unwrap()), time) + } + 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(date: &str) -> (types::Date, &str) { + ( + types::Date::Value(Date::strptime("'%Y-%m-%d'", date).unwrap()), + date, + ) + } + test_type( + "DATE", + &[ + make_check("'1970-01-01'"), + make_check("'1965-09-25'"), + make_check("'2010-02-09'"), + (types::Date::PosInfinity, "'infinity'"), + (types::Date::NegInfinity, "'-infinity'"), + ], + ) + .await; +} + +#[tokio::test] +async fn test_time_params() { + fn make_check(time: &str) -> (Option