From 20ecbd66c27229ab02f2831d3232fb8237c5d63c Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 11 Oct 2024 13:10:13 -0600 Subject: [PATCH 01/15] feat: bootstrap sqlx-jiff development Signed-off-by: tison --- Cargo.toml | 1 + sqlx-jiff/Cargo.toml | 21 +++++++++++++++++++++ sqlx-jiff/src/lib.rs | 2 ++ sqlx-jiff/src/postgres.rs | 0 4 files changed, 24 insertions(+) create mode 100644 sqlx-jiff/Cargo.toml create mode 100644 sqlx-jiff/src/lib.rs create mode 100644 sqlx-jiff/src/postgres.rs diff --git a/Cargo.toml b/Cargo.toml index 0b521e8b..e93e98af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ members = [ "jiff-cli", "jiff-tzdb", "jiff-tzdb-platform", + "sqlx-jiff", "examples/*", ] diff --git a/sqlx-jiff/Cargo.toml b/sqlx-jiff/Cargo.toml new file mode 100644 index 00000000..0b5daa55 --- /dev/null +++ b/sqlx-jiff/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "sqlx-jiff" +version = "0.1.0" +license = "Unlicense OR MIT" +homepage = "https://github.com/BurntSushi/jiff/tree/master/sqlx-jiff" +repository = "https://github.com/BurntSushi/jiff" +documentation = "https://docs.rs/sqlx-jiff" +description = "Integration to use jiff structs for datetime types in sqlx." +categories = ["date-and-time"] +keywords = ["date", "time", "temporal", "zone", "iana"] +workspace = ".." +edition = "2021" +rust-version = "1.70" + +[features] +default = [] +postgres = ["sqlx/postgres"] + +[dependencies] +jiff = { path = ".." } +sqlx = { version = "0.8.2" } diff --git a/sqlx-jiff/src/lib.rs b/sqlx-jiff/src/lib.rs new file mode 100644 index 00000000..e55fad4a --- /dev/null +++ b/sqlx-jiff/src/lib.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "postgres")] +mod postgres; \ No newline at end of file diff --git a/sqlx-jiff/src/postgres.rs b/sqlx-jiff/src/postgres.rs new file mode 100644 index 00000000..e69de29b From 89f63301f448630b2e22b14414bee0ac8d3128a8 Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 11 Oct 2024 13:15:11 -0600 Subject: [PATCH 02/15] builtin features instead Signed-off-by: tison --- Cargo.toml | 15 ++++++--- sqlx-jiff/Cargo.toml | 21 ------------ sqlx-jiff/src/lib.rs | 2 -- sqlx-jiff/src/postgres.rs | 0 src/lib.rs | 3 ++ src/sqlx_impls/mod.rs | 2 ++ src/sqlx_impls/postgres.rs | 69 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 84 insertions(+), 28 deletions(-) delete mode 100644 sqlx-jiff/Cargo.toml delete mode 100644 sqlx-jiff/src/lib.rs delete mode 100644 sqlx-jiff/src/postgres.rs create mode 100644 src/sqlx_impls/mod.rs create mode 100644 src/sqlx_impls/postgres.rs diff --git a/Cargo.toml b/Cargo.toml index e93e98af..11f0f13b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,11 +20,10 @@ rust-version = "1.70" [workspace] members = [ - "jiff-cli", - "jiff-tzdb", - "jiff-tzdb-platform", - "sqlx-jiff", - "examples/*", + "jiff-cli", + "jiff-tzdb", + "jiff-tzdb-platform", + "examples/*", ] # Features are documented in the "Crate features" section of the crate docs: @@ -75,11 +74,17 @@ tzdb-zoneinfo = ["std"] # (This is the same dependency setup that the `getrandom` crate uses.) js = ["dep:wasm-bindgen", "dep:js-sys"] +# This enables the integrations with the `sqlx` crate. +sqlx = ["dep:sqlx"] +sqlx-postgres = ["sqlx", "sqlx/postgres"] + [dependencies] jiff-tzdb = { version = "0.1.1", path = "jiff-tzdb", optional = true } log = { version = "0.4.21", optional = true } serde = { version = "1.0.203", optional = true } +sqlx = { version = "0.8.2", optional = true } + # Note that the `cfg` gate for the `tzdb-bundle-platform` must repeat the # target gate on this dependency. The intent is that `tzdb-bundle-platform` # is enabled by default, but that the `tzdb-bundle-platform` crate is only diff --git a/sqlx-jiff/Cargo.toml b/sqlx-jiff/Cargo.toml deleted file mode 100644 index 0b5daa55..00000000 --- a/sqlx-jiff/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "sqlx-jiff" -version = "0.1.0" -license = "Unlicense OR MIT" -homepage = "https://github.com/BurntSushi/jiff/tree/master/sqlx-jiff" -repository = "https://github.com/BurntSushi/jiff" -documentation = "https://docs.rs/sqlx-jiff" -description = "Integration to use jiff structs for datetime types in sqlx." -categories = ["date-and-time"] -keywords = ["date", "time", "temporal", "zone", "iana"] -workspace = ".." -edition = "2021" -rust-version = "1.70" - -[features] -default = [] -postgres = ["sqlx/postgres"] - -[dependencies] -jiff = { path = ".." } -sqlx = { version = "0.8.2" } diff --git a/sqlx-jiff/src/lib.rs b/sqlx-jiff/src/lib.rs deleted file mode 100644 index e55fad4a..00000000 --- a/sqlx-jiff/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(feature = "postgres")] -mod postgres; \ No newline at end of file diff --git a/sqlx-jiff/src/postgres.rs b/sqlx-jiff/src/postgres.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/lib.rs b/src/lib.rs index b0581867..ae154e8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -712,6 +712,9 @@ pub mod tz; mod util; mod zoned; +#[cfg(feature = "sqlx")] +mod sqlx_impls; + /// Longer form documentation for Jiff. pub mod _documentation { #[doc = include_str!("../COMPARE.md")] diff --git a/src/sqlx_impls/mod.rs b/src/sqlx_impls/mod.rs new file mode 100644 index 00000000..29bcf511 --- /dev/null +++ b/src/sqlx_impls/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "sqlx-postgres")] +mod postgres; diff --git a/src/sqlx_impls/postgres.rs b/src/sqlx_impls/postgres.rs new file mode 100644 index 00000000..abe422da --- /dev/null +++ b/src/sqlx_impls/postgres.rs @@ -0,0 +1,69 @@ +use crate::{SignedDuration, Timestamp}; +use core::str::FromStr; +use sqlx::encode::IsNull; +use sqlx::error::BoxDynError; +use sqlx::postgres::types::Oid; +use sqlx::postgres::{ + PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, +}; +use sqlx::{Database, Decode, Encode, Postgres, Type}; +use std::format; + +impl Type for Timestamp { + fn type_info() -> PgTypeInfo { + // 1184 => PgType::Timestamptz + PgTypeInfo::with_oid(Oid(1184)) + } +} + +impl PgHasArrayType for Timestamp { + fn array_type_info() -> PgTypeInfo { + // 1185 => PgType::TimestamptzArray + PgTypeInfo::with_oid(Oid(1185)) + } +} + +impl Encode<'_, Postgres> for Timestamp { + fn encode_by_ref( + &self, + buf: &mut PgArgumentBuffer, + ) -> Result { + // TIMESTAMP is encoded as the microseconds since the epoch + let micros = + self.duration_since(postgres_epoch_timestamp()).as_micros(); + let micros = i64::try_from(micros).map_err(|_| { + format!("Timestamp {} out of range for Postgres: {micros}", self) + })?; + Encode::::encode(micros, buf) + } + + fn size_hint(&self) -> usize { + size_of::() + } +} + +impl<'r> Decode<'r, Postgres> for Timestamp { + fn decode( + value: ::ValueRef<'r>, + ) -> Result { + Ok(match value.format() { + PgValueFormat::Binary => { + // TIMESTAMP is encoded as the microseconds since the epoch + let us = Decode::::decode(value)?; + let ts = postgres_epoch_timestamp() + .checked_add(SignedDuration::from_micros(us))?; + ts + } + PgValueFormat::Text => { + let s = value.as_str()?; + let ts = Timestamp::from_str(s)?; + ts + } + }) + } +} + +fn postgres_epoch_timestamp() -> Timestamp { + Timestamp::from_str("2000-01-01T00:00:00Z") + .expect("2000-01-01T00:00:00Z is a valid timestamp") +} From 309cc8d9fbd68577f4139a8df721d7dd177a7012 Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 11 Oct 2024 13:41:20 -0600 Subject: [PATCH 03/15] Revert "builtin features instead" This reverts commit 89f63301f448630b2e22b14414bee0ac8d3128a8. --- Cargo.toml | 15 +++------ sqlx-jiff/Cargo.toml | 21 ++++++++++++ sqlx-jiff/src/lib.rs | 2 ++ sqlx-jiff/src/postgres.rs | 0 src/lib.rs | 3 -- src/sqlx_impls/mod.rs | 2 -- src/sqlx_impls/postgres.rs | 69 -------------------------------------- 7 files changed, 28 insertions(+), 84 deletions(-) create mode 100644 sqlx-jiff/Cargo.toml create mode 100644 sqlx-jiff/src/lib.rs create mode 100644 sqlx-jiff/src/postgres.rs delete mode 100644 src/sqlx_impls/mod.rs delete mode 100644 src/sqlx_impls/postgres.rs diff --git a/Cargo.toml b/Cargo.toml index 11f0f13b..e93e98af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,10 +20,11 @@ rust-version = "1.70" [workspace] members = [ - "jiff-cli", - "jiff-tzdb", - "jiff-tzdb-platform", - "examples/*", + "jiff-cli", + "jiff-tzdb", + "jiff-tzdb-platform", + "sqlx-jiff", + "examples/*", ] # Features are documented in the "Crate features" section of the crate docs: @@ -74,17 +75,11 @@ tzdb-zoneinfo = ["std"] # (This is the same dependency setup that the `getrandom` crate uses.) js = ["dep:wasm-bindgen", "dep:js-sys"] -# This enables the integrations with the `sqlx` crate. -sqlx = ["dep:sqlx"] -sqlx-postgres = ["sqlx", "sqlx/postgres"] - [dependencies] jiff-tzdb = { version = "0.1.1", path = "jiff-tzdb", optional = true } log = { version = "0.4.21", optional = true } serde = { version = "1.0.203", optional = true } -sqlx = { version = "0.8.2", optional = true } - # Note that the `cfg` gate for the `tzdb-bundle-platform` must repeat the # target gate on this dependency. The intent is that `tzdb-bundle-platform` # is enabled by default, but that the `tzdb-bundle-platform` crate is only diff --git a/sqlx-jiff/Cargo.toml b/sqlx-jiff/Cargo.toml new file mode 100644 index 00000000..0b5daa55 --- /dev/null +++ b/sqlx-jiff/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "sqlx-jiff" +version = "0.1.0" +license = "Unlicense OR MIT" +homepage = "https://github.com/BurntSushi/jiff/tree/master/sqlx-jiff" +repository = "https://github.com/BurntSushi/jiff" +documentation = "https://docs.rs/sqlx-jiff" +description = "Integration to use jiff structs for datetime types in sqlx." +categories = ["date-and-time"] +keywords = ["date", "time", "temporal", "zone", "iana"] +workspace = ".." +edition = "2021" +rust-version = "1.70" + +[features] +default = [] +postgres = ["sqlx/postgres"] + +[dependencies] +jiff = { path = ".." } +sqlx = { version = "0.8.2" } diff --git a/sqlx-jiff/src/lib.rs b/sqlx-jiff/src/lib.rs new file mode 100644 index 00000000..e55fad4a --- /dev/null +++ b/sqlx-jiff/src/lib.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "postgres")] +mod postgres; \ No newline at end of file diff --git a/sqlx-jiff/src/postgres.rs b/sqlx-jiff/src/postgres.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/lib.rs b/src/lib.rs index ae154e8d..b0581867 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -712,9 +712,6 @@ pub mod tz; mod util; mod zoned; -#[cfg(feature = "sqlx")] -mod sqlx_impls; - /// Longer form documentation for Jiff. pub mod _documentation { #[doc = include_str!("../COMPARE.md")] diff --git a/src/sqlx_impls/mod.rs b/src/sqlx_impls/mod.rs deleted file mode 100644 index 29bcf511..00000000 --- a/src/sqlx_impls/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(feature = "sqlx-postgres")] -mod postgres; diff --git a/src/sqlx_impls/postgres.rs b/src/sqlx_impls/postgres.rs deleted file mode 100644 index abe422da..00000000 --- a/src/sqlx_impls/postgres.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::{SignedDuration, Timestamp}; -use core::str::FromStr; -use sqlx::encode::IsNull; -use sqlx::error::BoxDynError; -use sqlx::postgres::types::Oid; -use sqlx::postgres::{ - PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, -}; -use sqlx::{Database, Decode, Encode, Postgres, Type}; -use std::format; - -impl Type for Timestamp { - fn type_info() -> PgTypeInfo { - // 1184 => PgType::Timestamptz - PgTypeInfo::with_oid(Oid(1184)) - } -} - -impl PgHasArrayType for Timestamp { - fn array_type_info() -> PgTypeInfo { - // 1185 => PgType::TimestamptzArray - PgTypeInfo::with_oid(Oid(1185)) - } -} - -impl Encode<'_, Postgres> for Timestamp { - fn encode_by_ref( - &self, - buf: &mut PgArgumentBuffer, - ) -> Result { - // TIMESTAMP is encoded as the microseconds since the epoch - let micros = - self.duration_since(postgres_epoch_timestamp()).as_micros(); - let micros = i64::try_from(micros).map_err(|_| { - format!("Timestamp {} out of range for Postgres: {micros}", self) - })?; - Encode::::encode(micros, buf) - } - - fn size_hint(&self) -> usize { - size_of::() - } -} - -impl<'r> Decode<'r, Postgres> for Timestamp { - fn decode( - value: ::ValueRef<'r>, - ) -> Result { - Ok(match value.format() { - PgValueFormat::Binary => { - // TIMESTAMP is encoded as the microseconds since the epoch - let us = Decode::::decode(value)?; - let ts = postgres_epoch_timestamp() - .checked_add(SignedDuration::from_micros(us))?; - ts - } - PgValueFormat::Text => { - let s = value.as_str()?; - let ts = Timestamp::from_str(s)?; - ts - } - }) - } -} - -fn postgres_epoch_timestamp() -> Timestamp { - Timestamp::from_str("2000-01-01T00:00:00Z") - .expect("2000-01-01T00:00:00Z is a valid timestamp") -} From 1b248f2e77a3c7584616c850272aebf3cf479a40 Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 11 Oct 2024 13:46:45 -0600 Subject: [PATCH 04/15] implement integration as a separate crate Signed-off-by: tison --- sqlx-jiff/Cargo.toml | 3 +- sqlx-jiff/src/lib.rs | 34 ++++++++++++++++++- sqlx-jiff/src/postgres.rs | 69 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/sqlx-jiff/Cargo.toml b/sqlx-jiff/Cargo.toml index 0b5daa55..67d3837f 100644 --- a/sqlx-jiff/Cargo.toml +++ b/sqlx-jiff/Cargo.toml @@ -18,4 +18,5 @@ postgres = ["sqlx/postgres"] [dependencies] jiff = { path = ".." } -sqlx = { version = "0.8.2" } +serde = { version = "1.0" } +sqlx = { version = "0.8" } diff --git a/sqlx-jiff/src/lib.rs b/sqlx-jiff/src/lib.rs index e55fad4a..b5cab558 100644 --- a/sqlx-jiff/src/lib.rs +++ b/sqlx-jiff/src/lib.rs @@ -1,2 +1,34 @@ +use serde::{Deserialize, Serialize}; +use std::ops::{Deref, DerefMut}; + #[cfg(feature = "postgres")] -mod postgres; \ No newline at end of file +mod postgres; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Timestamp(pub jiff::Timestamp); + +impl From for jiff::Timestamp { + fn from(ts: Timestamp) -> Self { + ts.0 + } +} + +impl From for Timestamp { + fn from(ts: jiff::Timestamp) -> Self { + Self(ts) + } +} + +impl Deref for Timestamp { + type Target = jiff::Timestamp; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Timestamp { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/sqlx-jiff/src/postgres.rs b/sqlx-jiff/src/postgres.rs index e69de29b..6ad36a0a 100644 --- a/sqlx-jiff/src/postgres.rs +++ b/sqlx-jiff/src/postgres.rs @@ -0,0 +1,69 @@ +use crate::Timestamp; +use jiff::SignedDuration; +use sqlx::encode::IsNull; +use sqlx::error::BoxDynError; +use sqlx::postgres::types::Oid; +use sqlx::postgres::{ + PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, +}; +use sqlx::{Database, Decode, Encode, Postgres, Type}; +use std::str::FromStr; + +impl Type for Timestamp { + fn type_info() -> PgTypeInfo { + // 1184 => PgType::Timestamptz + PgTypeInfo::with_oid(Oid(1184)) + } +} + +impl PgHasArrayType for Timestamp { + fn array_type_info() -> PgTypeInfo { + // 1185 => PgType::TimestamptzArray + PgTypeInfo::with_oid(Oid(1185)) + } +} + +impl Encode<'_, Postgres> for Timestamp { + fn encode_by_ref( + &self, + buf: &mut PgArgumentBuffer, + ) -> Result { + // TIMESTAMP is encoded as the microseconds since the epoch + let micros = + self.0.duration_since(postgres_epoch_timestamp()).as_micros(); + let micros = i64::try_from(micros).map_err(|_| { + format!("Timestamp {} out of range for Postgres: {micros}", self.0) + })?; + Encode::::encode(micros, buf) + } + + fn size_hint(&self) -> usize { + size_of::() + } +} + +impl<'r> Decode<'r, Postgres> for Timestamp { + fn decode( + value: ::ValueRef<'r>, + ) -> Result { + Ok(match value.format() { + PgValueFormat::Binary => { + // TIMESTAMP is encoded as the microseconds since the epoch + let us = Decode::::decode(value)?; + let ts = postgres_epoch_timestamp() + .checked_add(SignedDuration::from_micros(us))?; + Timestamp(ts) + } + PgValueFormat::Text => { + let s = value.as_str()?; + let ts = jiff::Timestamp::from_str(s)?; + Timestamp(ts) + } + }) + } +} + +fn postgres_epoch_timestamp() -> jiff::Timestamp { + jiff::Timestamp::from_str("2000-01-01T00:00:00Z") + .expect("2000-01-01T00:00:00Z is a valid timestamp") +} From bd6bdcdba3badf743c22734644a19f64afac18db Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 11 Oct 2024 14:03:01 -0600 Subject: [PATCH 05/15] no pub field but to_jiff and to_sqlx Signed-off-by: tison --- sqlx-jiff/src/lib.rs | 31 +++--------------------- sqlx-jiff/src/wrap_types.rs | 47 +++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 sqlx-jiff/src/wrap_types.rs diff --git a/sqlx-jiff/src/lib.rs b/sqlx-jiff/src/lib.rs index b5cab558..595ed7d8 100644 --- a/sqlx-jiff/src/lib.rs +++ b/sqlx-jiff/src/lib.rs @@ -4,31 +4,6 @@ use std::ops::{Deref, DerefMut}; #[cfg(feature = "postgres")] mod postgres; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct Timestamp(pub jiff::Timestamp); - -impl From for jiff::Timestamp { - fn from(ts: Timestamp) -> Self { - ts.0 - } -} - -impl From for Timestamp { - fn from(ts: jiff::Timestamp) -> Self { - Self(ts) - } -} - -impl Deref for Timestamp { - type Target = jiff::Timestamp; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Timestamp { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} +mod wrap_types; +pub use wrap_types::Timestamp; +pub use wrap_types::ToTimestamp; diff --git a/sqlx-jiff/src/wrap_types.rs b/sqlx-jiff/src/wrap_types.rs new file mode 100644 index 00000000..6d18f75b --- /dev/null +++ b/sqlx-jiff/src/wrap_types.rs @@ -0,0 +1,47 @@ +use serde::{Deserialize, Serialize}; +use std::ops::{Deref, DerefMut}; + +pub trait ToTimestamp { + fn to_sqlx(self) -> Timestamp; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub struct Timestamp(jiff::Timestamp); + +impl Timestamp { + pub fn to_jiff(self) -> jiff::Timestamp { + self.0 + } +} + +impl ToTimestamp for jiff::Timestamp { + fn to_sqlx(self) -> Timestamp { + Timestamp(self) + } +} + +impl From for jiff::Timestamp { + fn from(ts: Timestamp) -> Self { + ts.0 + } +} + +impl From for Timestamp { + fn from(ts: jiff::Timestamp) -> Self { + Self(ts) + } +} + +impl Deref for Timestamp { + type Target = jiff::Timestamp; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Timestamp { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} From de9e83b05b13cdb1981afd6b5b6c2212f9a1a679 Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 11 Oct 2024 14:04:50 -0600 Subject: [PATCH 06/15] sqlx-jiff to jiff-sqlx Signed-off-by: tison --- Cargo.toml | 2 +- {sqlx-jiff => jiff-sqlx}/Cargo.toml | 6 +++--- {sqlx-jiff => jiff-sqlx}/src/lib.rs | 0 {sqlx-jiff => jiff-sqlx}/src/postgres.rs | 0 {sqlx-jiff => jiff-sqlx}/src/wrap_types.rs | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename {sqlx-jiff => jiff-sqlx}/Cargo.toml (78%) rename {sqlx-jiff => jiff-sqlx}/src/lib.rs (100%) rename {sqlx-jiff => jiff-sqlx}/src/postgres.rs (100%) rename {sqlx-jiff => jiff-sqlx}/src/wrap_types.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index e93e98af..e3e91087 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ "jiff-cli", "jiff-tzdb", "jiff-tzdb-platform", - "sqlx-jiff", + "jiff-sqlx", "examples/*", ] diff --git a/sqlx-jiff/Cargo.toml b/jiff-sqlx/Cargo.toml similarity index 78% rename from sqlx-jiff/Cargo.toml rename to jiff-sqlx/Cargo.toml index 67d3837f..32a178ae 100644 --- a/sqlx-jiff/Cargo.toml +++ b/jiff-sqlx/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "sqlx-jiff" +name = "jiff-sqlx" version = "0.1.0" license = "Unlicense OR MIT" -homepage = "https://github.com/BurntSushi/jiff/tree/master/sqlx-jiff" +homepage = "https://github.com/BurntSushi/jiff/tree/master/jiff-sqlx" repository = "https://github.com/BurntSushi/jiff" -documentation = "https://docs.rs/sqlx-jiff" +documentation = "https://docs.rs/jiff-sqlx" description = "Integration to use jiff structs for datetime types in sqlx." categories = ["date-and-time"] keywords = ["date", "time", "temporal", "zone", "iana"] diff --git a/sqlx-jiff/src/lib.rs b/jiff-sqlx/src/lib.rs similarity index 100% rename from sqlx-jiff/src/lib.rs rename to jiff-sqlx/src/lib.rs diff --git a/sqlx-jiff/src/postgres.rs b/jiff-sqlx/src/postgres.rs similarity index 100% rename from sqlx-jiff/src/postgres.rs rename to jiff-sqlx/src/postgres.rs diff --git a/sqlx-jiff/src/wrap_types.rs b/jiff-sqlx/src/wrap_types.rs similarity index 100% rename from sqlx-jiff/src/wrap_types.rs rename to jiff-sqlx/src/wrap_types.rs From b6372d37dcb31ee5dbe5d6a93eafd6028a69200a Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 11 Oct 2024 14:09:31 -0600 Subject: [PATCH 07/15] minimize the implementation Signed-off-by: tison --- jiff-sqlx/Cargo.toml | 1 - jiff-sqlx/src/lib.rs | 3 --- jiff-sqlx/src/postgres.rs | 13 +++++++------ jiff-sqlx/src/wrap_types.rs | 19 +------------------ 4 files changed, 8 insertions(+), 28 deletions(-) diff --git a/jiff-sqlx/Cargo.toml b/jiff-sqlx/Cargo.toml index 32a178ae..953560e2 100644 --- a/jiff-sqlx/Cargo.toml +++ b/jiff-sqlx/Cargo.toml @@ -18,5 +18,4 @@ postgres = ["sqlx/postgres"] [dependencies] jiff = { path = ".." } -serde = { version = "1.0" } sqlx = { version = "0.8" } diff --git a/jiff-sqlx/src/lib.rs b/jiff-sqlx/src/lib.rs index 595ed7d8..520a9241 100644 --- a/jiff-sqlx/src/lib.rs +++ b/jiff-sqlx/src/lib.rs @@ -1,6 +1,3 @@ -use serde::{Deserialize, Serialize}; -use std::ops::{Deref, DerefMut}; - #[cfg(feature = "postgres")] mod postgres; diff --git a/jiff-sqlx/src/postgres.rs b/jiff-sqlx/src/postgres.rs index 6ad36a0a..35baf275 100644 --- a/jiff-sqlx/src/postgres.rs +++ b/jiff-sqlx/src/postgres.rs @@ -1,4 +1,4 @@ -use crate::Timestamp; +use crate::{Timestamp, ToTimestamp}; use jiff::SignedDuration; use sqlx::encode::IsNull; use sqlx::error::BoxDynError; @@ -28,11 +28,12 @@ impl Encode<'_, Postgres> for Timestamp { &self, buf: &mut PgArgumentBuffer, ) -> Result { + let ts = self.to_jiff(); + // TIMESTAMP is encoded as the microseconds since the epoch - let micros = - self.0.duration_since(postgres_epoch_timestamp()).as_micros(); + let micros = ts.duration_since(postgres_epoch_timestamp()).as_micros(); let micros = i64::try_from(micros).map_err(|_| { - format!("Timestamp {} out of range for Postgres: {micros}", self.0) + format!("Timestamp {ts} out of range for Postgres: {micros}") })?; Encode::::encode(micros, buf) } @@ -52,12 +53,12 @@ impl<'r> Decode<'r, Postgres> for Timestamp { let us = Decode::::decode(value)?; let ts = postgres_epoch_timestamp() .checked_add(SignedDuration::from_micros(us))?; - Timestamp(ts) + ts.to_sqlx() } PgValueFormat::Text => { let s = value.as_str()?; let ts = jiff::Timestamp::from_str(s)?; - Timestamp(ts) + ts.to_sqlx() } }) } diff --git a/jiff-sqlx/src/wrap_types.rs b/jiff-sqlx/src/wrap_types.rs index 6d18f75b..81047a05 100644 --- a/jiff-sqlx/src/wrap_types.rs +++ b/jiff-sqlx/src/wrap_types.rs @@ -1,11 +1,8 @@ -use serde::{Deserialize, Serialize}; -use std::ops::{Deref, DerefMut}; - pub trait ToTimestamp { fn to_sqlx(self) -> Timestamp; } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy)] pub struct Timestamp(jiff::Timestamp); impl Timestamp { @@ -31,17 +28,3 @@ impl From for Timestamp { Self(ts) } } - -impl Deref for Timestamp { - type Target = jiff::Timestamp; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Timestamp { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} From 4a6548f6e73e99afd407db6d32441a24503c1132 Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 23 Oct 2024 14:16:20 +0800 Subject: [PATCH 08/15] wrap SignedDuration Signed-off-by: tison --- jiff-sqlx/src/wrap_types.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/jiff-sqlx/src/wrap_types.rs b/jiff-sqlx/src/wrap_types.rs index 81047a05..eac556f9 100644 --- a/jiff-sqlx/src/wrap_types.rs +++ b/jiff-sqlx/src/wrap_types.rs @@ -28,3 +28,34 @@ impl From for Timestamp { Self(ts) } } + +pub trait ToSignedDuration { + fn to_sqlx(self) -> SignedDuration; +} + +#[derive(Debug, Clone, Copy)] +pub struct SignedDuration(jiff::SignedDuration); + +impl SignedDuration { + pub fn to_jiff(self) -> jiff::SignedDuration { + self.0 + } +} + +impl ToSignedDuration for jiff::SignedDuration { + fn to_sqlx(self) -> SignedDuration { + SignedDuration(self) + } +} + +impl From for jiff::SignedDuration { + fn from(sd: SignedDuration) -> Self { + sd.0 + } +} + +impl From for SignedDuration { + fn from(sd: jiff::SignedDuration) -> Self { + Self(sd) + } +} From c3ee65bded7e97bd33458597b138f8be3d04d381 Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 23 Oct 2024 14:26:31 +0800 Subject: [PATCH 09/15] bridge Interval to SignedDuration Signed-off-by: tison --- jiff-sqlx/src/lib.rs | 3 +- jiff-sqlx/src/postgres/interval.rs | 110 ++++++++++++++++++ jiff-sqlx/src/postgres/mod.rs | 2 + .../{postgres.rs => postgres/timestamp.rs} | 0 4 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 jiff-sqlx/src/postgres/interval.rs create mode 100644 jiff-sqlx/src/postgres/mod.rs rename jiff-sqlx/src/{postgres.rs => postgres/timestamp.rs} (100%) diff --git a/jiff-sqlx/src/lib.rs b/jiff-sqlx/src/lib.rs index 520a9241..8a5e6f6c 100644 --- a/jiff-sqlx/src/lib.rs +++ b/jiff-sqlx/src/lib.rs @@ -2,5 +2,4 @@ mod postgres; mod wrap_types; -pub use wrap_types::Timestamp; -pub use wrap_types::ToTimestamp; +pub use wrap_types::*; diff --git a/jiff-sqlx/src/postgres/interval.rs b/jiff-sqlx/src/postgres/interval.rs new file mode 100644 index 00000000..6686aac6 --- /dev/null +++ b/jiff-sqlx/src/postgres/interval.rs @@ -0,0 +1,110 @@ +use crate::SignedDuration; +use sqlx::encode::IsNull; +use sqlx::error::BoxDynError; +use sqlx::postgres::types::{Oid, PgInterval}; +use sqlx::postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo}; +use sqlx::{Encode, Postgres, Type}; + +impl Type for SignedDuration { + fn type_info() -> PgTypeInfo { + // 1186 => PgType::Interval + PgTypeInfo::with_oid(Oid(1186)) + } +} + +impl PgHasArrayType for SignedDuration { + fn array_type_info() -> PgTypeInfo { + // 1187 => PgType::IntervalArray + PgTypeInfo::with_oid(Oid(1187)) + } +} + +impl TryFrom for PgInterval { + type Error = BoxDynError; + + /// Convert a `SignedDuration` to a `PgInterval`. + /// + /// This returns an error if there is a loss of precision using nanoseconds or if there is a + /// microseconds overflow. + fn try_from(value: SignedDuration) -> Result { + let value = value.to_jiff(); + + if value.subsec_nanos() % 1000 != 0 { + return Err( + "PostgreSQL `INTERVAL` does not support nanoseconds precision" + .into(), + ); + } + + let micros = value.as_micros(); + if micros >= i64::MIN as i128 && micros <= i64::MAX as i128 { + Ok(Self { months: 0, days: 0, microseconds: micros as i64 }) + } else { + Err("Overflow has occurred for PostgreSQL `INTERVAL`".into()) + } + } +} + +impl Encode<'_, Postgres> for SignedDuration { + fn encode_by_ref( + &self, + buf: &mut PgArgumentBuffer, + ) -> Result { + let pg_interval = PgInterval::try_from(*self)?; + pg_interval.encode_by_ref(buf) + } + + fn size_hint(&self) -> usize { + 2 * size_of::() + } +} + +#[cfg(test)] +mod tests { + use crate::ToSignedDuration; + use sqlx::postgres::types::PgInterval; + + #[test] + fn test_pginterval_jiff() { + // Case for positive duration + let interval = PgInterval { days: 0, months: 0, microseconds: 27_000 }; + assert_eq!( + &PgInterval::try_from( + jiff::SignedDuration::from_micros(27_000).to_sqlx() + ) + .unwrap(), + &interval + ); + + // Case for negative duration + let interval = + PgInterval { days: 0, months: 0, microseconds: -27_000 }; + assert_eq!( + &PgInterval::try_from( + jiff::SignedDuration::from_micros(-27_000).to_sqlx() + ) + .unwrap(), + &interval + ); + + // Case when precision loss occurs + assert!(PgInterval::try_from( + jiff::SignedDuration::from_nanos(27_000_001).to_sqlx() + ) + .is_err()); + assert!(PgInterval::try_from( + jiff::SignedDuration::from_nanos(-27_000_001).to_sqlx() + ) + .is_err()); + + // Case when microseconds overflow occurs + assert!(PgInterval::try_from( + jiff::SignedDuration::from_secs(10_000_000_000_000).to_sqlx() + ) + .is_err()); + assert!(PgInterval::try_from( + jiff::SignedDuration::from_secs(-10_000_000_000_000).to_sqlx() + ) + .is_err()); + } +} diff --git a/jiff-sqlx/src/postgres/mod.rs b/jiff-sqlx/src/postgres/mod.rs new file mode 100644 index 00000000..6d496beb --- /dev/null +++ b/jiff-sqlx/src/postgres/mod.rs @@ -0,0 +1,2 @@ +mod interval; +mod timestamp; \ No newline at end of file diff --git a/jiff-sqlx/src/postgres.rs b/jiff-sqlx/src/postgres/timestamp.rs similarity index 100% rename from jiff-sqlx/src/postgres.rs rename to jiff-sqlx/src/postgres/timestamp.rs From d8472eb3ac0d7cac8c5dc443ea2d12a8e05d8504 Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 23 Oct 2024 14:34:58 +0800 Subject: [PATCH 10/15] wrap civil Date and Time Signed-off-by: tison --- jiff-sqlx/src/postgres/date.rs | 0 jiff-sqlx/src/postgres/datetime.rs | 0 jiff-sqlx/src/postgres/mod.rs | 3 + jiff-sqlx/src/postgres/time.rs | 0 jiff-sqlx/src/wrap_types.rs | 101 ++++++++++++----------------- 5 files changed, 43 insertions(+), 61 deletions(-) create mode 100644 jiff-sqlx/src/postgres/date.rs create mode 100644 jiff-sqlx/src/postgres/datetime.rs create mode 100644 jiff-sqlx/src/postgres/time.rs diff --git a/jiff-sqlx/src/postgres/date.rs b/jiff-sqlx/src/postgres/date.rs new file mode 100644 index 00000000..e69de29b diff --git a/jiff-sqlx/src/postgres/datetime.rs b/jiff-sqlx/src/postgres/datetime.rs new file mode 100644 index 00000000..e69de29b diff --git a/jiff-sqlx/src/postgres/mod.rs b/jiff-sqlx/src/postgres/mod.rs index 6d496beb..e0bc0c1c 100644 --- a/jiff-sqlx/src/postgres/mod.rs +++ b/jiff-sqlx/src/postgres/mod.rs @@ -1,2 +1,5 @@ +mod date; +mod datetime; mod interval; +mod time; mod timestamp; \ No newline at end of file diff --git a/jiff-sqlx/src/postgres/time.rs b/jiff-sqlx/src/postgres/time.rs new file mode 100644 index 00000000..e69de29b diff --git a/jiff-sqlx/src/wrap_types.rs b/jiff-sqlx/src/wrap_types.rs index eac556f9..f218adef 100644 --- a/jiff-sqlx/src/wrap_types.rs +++ b/jiff-sqlx/src/wrap_types.rs @@ -1,61 +1,40 @@ -pub trait ToTimestamp { - fn to_sqlx(self) -> Timestamp; -} - -#[derive(Debug, Clone, Copy)] -pub struct Timestamp(jiff::Timestamp); - -impl Timestamp { - pub fn to_jiff(self) -> jiff::Timestamp { - self.0 - } -} - -impl ToTimestamp for jiff::Timestamp { - fn to_sqlx(self) -> Timestamp { - Timestamp(self) - } -} - -impl From for jiff::Timestamp { - fn from(ts: Timestamp) -> Self { - ts.0 - } -} - -impl From for Timestamp { - fn from(ts: jiff::Timestamp) -> Self { - Self(ts) - } -} - -pub trait ToSignedDuration { - fn to_sqlx(self) -> SignedDuration; -} - -#[derive(Debug, Clone, Copy)] -pub struct SignedDuration(jiff::SignedDuration); - -impl SignedDuration { - pub fn to_jiff(self) -> jiff::SignedDuration { - self.0 - } -} - -impl ToSignedDuration for jiff::SignedDuration { - fn to_sqlx(self) -> SignedDuration { - SignedDuration(self) - } -} - -impl From for jiff::SignedDuration { - fn from(sd: SignedDuration) -> Self { - sd.0 - } -} - -impl From for SignedDuration { - fn from(sd: jiff::SignedDuration) -> Self { - Self(sd) - } -} +macro_rules! define_wrap_type { + ($wrapper:ident, $wrapper_trait:ident, $origin:ty) => { + pub trait $wrapper_trait { + fn to_sqlx(self) -> $wrapper; + } + + #[derive(Debug, Clone, Copy)] + pub struct $wrapper($origin); + + impl $wrapper { + pub fn to_jiff(self) -> $origin { + self.0 + } + } + + impl $wrapper_trait for $origin { + fn to_sqlx(self) -> $wrapper { + $wrapper(self) + } + } + + impl From<$wrapper> for $origin { + fn from(value: $wrapper) -> Self { + value.0 + } + } + + impl From<$origin> for $wrapper { + fn from(value: $origin) -> Self { + Self(value) + } + } + }; +} + +define_wrap_type!(Timestamp, ToTimestamp, jiff::Timestamp); +define_wrap_type!(SignedDuration, ToSignedDuration, jiff::SignedDuration); +define_wrap_type!(Date, ToDate, jiff::civil::Date); +define_wrap_type!(Time, ToTime, jiff::civil::Time); +define_wrap_type!(DateTime, ToDateTime, jiff::civil::DateTime); From ab4f1afc9e5b38b8f9cf8203bdaa30e7b46a29f4 Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 27 Oct 2024 01:52:56 +0800 Subject: [PATCH 11/15] bridge Date and Time Signed-off-by: tison --- jiff-sqlx/src/postgres/date.rs | 63 +++++++++++++++++++++++++++ jiff-sqlx/src/postgres/datetime.rs | 68 ++++++++++++++++++++++++++++++ jiff-sqlx/src/postgres/mod.rs | 2 +- jiff-sqlx/src/postgres/time.rs | 63 +++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 1 deletion(-) diff --git a/jiff-sqlx/src/postgres/date.rs b/jiff-sqlx/src/postgres/date.rs index e69de29b..c39cd53a 100644 --- a/jiff-sqlx/src/postgres/date.rs +++ b/jiff-sqlx/src/postgres/date.rs @@ -0,0 +1,63 @@ +use crate::{Date, ToDate}; +use sqlx::encode::IsNull; +use sqlx::error::BoxDynError; +use sqlx::postgres::types::Oid; +use sqlx::postgres::{ + PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, +}; +use sqlx::{Decode, Encode, Postgres, Type}; + +impl Type for Date { + fn type_info() -> PgTypeInfo { + // 1082 => PgType::Date + PgTypeInfo::with_oid(Oid(1082)) + } +} + +impl PgHasArrayType for Date { + fn array_type_info() -> PgTypeInfo { + // 1182 => PgType::DateArray + PgTypeInfo::with_oid(Oid(1182)) + } +} + +impl Encode<'_, Postgres> for Date { + fn encode_by_ref( + &self, + buf: &mut PgArgumentBuffer, + ) -> Result { + let date = self.to_jiff(); + + // DATE is encoded as the days since epoch + let days = date.since(postgres_epoch_date())?.get_days(); + Encode::::encode(days, buf) + } + + fn size_hint(&self) -> usize { + size_of::() + } +} + +impl<'r> Decode<'r, Postgres> for Date { + fn decode(value: PgValueRef<'r>) -> Result { + Ok(match value.format() { + PgValueFormat::Binary => { + // DATE is encoded as the days since epoch + let days: i32 = Decode::::decode(value)?; + let date = jiff::Span::new() + .try_days(days) + .and_then(|s| postgres_epoch_date().checked_add(s))?; + date.to_sqlx() + } + PgValueFormat::Text => { + let s = value.as_str()?; + let date = jiff::civil::Date::strptime("%Y-%m-%d", s)?; + date.to_sqlx() + } + }) + } +} + +const fn postgres_epoch_date() -> jiff::civil::Date { + jiff::civil::Date::constant(2000, 1, 1) +} diff --git a/jiff-sqlx/src/postgres/datetime.rs b/jiff-sqlx/src/postgres/datetime.rs index e69de29b..d1821544 100644 --- a/jiff-sqlx/src/postgres/datetime.rs +++ b/jiff-sqlx/src/postgres/datetime.rs @@ -0,0 +1,68 @@ +use crate::{DateTime, ToDateTime}; +use jiff::SignedDuration; +use sqlx::encode::IsNull; +use sqlx::error::BoxDynError; +use sqlx::postgres::types::Oid; +use sqlx::postgres::{ + PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, +}; +use sqlx::{Decode, Encode, Postgres, Type}; +use std::str::FromStr; + +impl Type for DateTime { + fn type_info() -> PgTypeInfo { + // 1114 => PgType::Timestamp + PgTypeInfo::with_oid(Oid(1114)) + } +} + +impl PgHasArrayType for DateTime { + fn array_type_info() -> PgTypeInfo { + // 1115 => PgType::TimestampArray + PgTypeInfo::with_oid(Oid(1115)) + } +} + +impl Encode<'_, Postgres> for DateTime { + fn encode_by_ref( + &self, + buf: &mut PgArgumentBuffer, + ) -> Result { + let datetime = self.to_jiff(); + + // TIMESTAMP is encoded as the microseconds since the epoch + let micros = + datetime.duration_since(postgres_epoch_datetime()).as_micros(); + let micros = i64::try_from(micros).map_err(|_| { + format!("DateTime {datetime} out of range for Postgres: {micros}") + })?; + Encode::::encode(micros, buf) + } + + fn size_hint(&self) -> usize { + size_of::() + } +} + +impl<'r> Decode<'r, Postgres> for DateTime { + fn decode(value: PgValueRef<'r>) -> Result { + Ok(match value.format() { + PgValueFormat::Binary => { + // TIMESTAMP is encoded as the microseconds since the epoch + let us = Decode::::decode(value)?; + let datetime = postgres_epoch_datetime() + .checked_add(SignedDuration::from_micros(us))?; + datetime.to_sqlx() + } + PgValueFormat::Text => { + let s = value.as_str()?; + let datetime = jiff::civil::DateTime::from_str(s)?; + datetime.to_sqlx() + } + }) + } +} + +const fn postgres_epoch_datetime() -> jiff::civil::DateTime { + jiff::civil::DateTime::constant(2000, 1, 1, 0, 0, 0, 0) +} diff --git a/jiff-sqlx/src/postgres/mod.rs b/jiff-sqlx/src/postgres/mod.rs index e0bc0c1c..d7750fc8 100644 --- a/jiff-sqlx/src/postgres/mod.rs +++ b/jiff-sqlx/src/postgres/mod.rs @@ -2,4 +2,4 @@ mod date; mod datetime; mod interval; mod time; -mod timestamp; \ No newline at end of file +mod timestamp; diff --git a/jiff-sqlx/src/postgres/time.rs b/jiff-sqlx/src/postgres/time.rs index e69de29b..ef2a349e 100644 --- a/jiff-sqlx/src/postgres/time.rs +++ b/jiff-sqlx/src/postgres/time.rs @@ -0,0 +1,63 @@ +use crate::{Time, ToTime}; +use jiff::SignedDuration; +use sqlx::encode::IsNull; +use sqlx::error::BoxDynError; +use sqlx::postgres::types::Oid; +use sqlx::postgres::{ + PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, +}; +use sqlx::{Decode, Encode, Postgres, Type}; + +impl Type for Time { + fn type_info() -> PgTypeInfo { + // 1083 => PgType::Time + PgTypeInfo::with_oid(Oid(1083)) + } +} + +impl PgHasArrayType for Time { + fn array_type_info() -> PgTypeInfo { + // 1183 => PgType::TimeArray + PgTypeInfo::with_oid(Oid(1183)) + } +} + +impl Encode<'_, Postgres> for Time { + fn encode_by_ref( + &self, + buf: &mut PgArgumentBuffer, + ) -> Result { + let time = self.to_jiff(); + + // TIME is encoded as the microseconds since midnight + let micros = + time.duration_since(jiff::civil::Time::midnight()).as_micros(); + let micros = i64::try_from(micros).map_err(|_| { + format!("Time {time} out of range for Postgres: {micros}") + })?; + Encode::::encode(micros, buf) + } + + fn size_hint(&self) -> usize { + size_of::() + } +} + +impl<'r> Decode<'r, Postgres> for Time { + fn decode(value: PgValueRef<'r>) -> Result { + Ok(match value.format() { + PgValueFormat::Binary => { + // TIME is encoded as the microseconds since midnight + let us: i64 = Decode::::decode(value)?; + let time = jiff::civil::Time::midnight() + .checked_add(SignedDuration::from_micros(us))?; + time.to_sqlx() + } + PgValueFormat::Text => { + let s = value.as_str()?; + let time = jiff::civil::Time::strptime("%H:%M:%S%.f", s)?; + time.to_sqlx() + } + }) + } +} From 54509f0949ff9c73747e42b26073e7fe2c83aa18 Mon Sep 17 00:00:00 2001 From: tison Date: Fri, 22 Nov 2024 22:35:54 +0800 Subject: [PATCH 12/15] no default sqlx features Signed-off-by: tison --- jiff-sqlx/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jiff-sqlx/Cargo.toml b/jiff-sqlx/Cargo.toml index 953560e2..288887cb 100644 --- a/jiff-sqlx/Cargo.toml +++ b/jiff-sqlx/Cargo.toml @@ -18,4 +18,4 @@ postgres = ["sqlx/postgres"] [dependencies] jiff = { path = ".." } -sqlx = { version = "0.8" } +sqlx = { version = "0.8", default-features = false } From 2f17955624151a5327365c0d5d7bf597cf15cacd Mon Sep 17 00:00:00 2001 From: tison Date: Thu, 2 Jan 2025 19:35:07 +0800 Subject: [PATCH 13/15] impl Decode for SignedDuration Signed-off-by: tison --- jiff-sqlx/src/postgres/interval.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/jiff-sqlx/src/postgres/interval.rs b/jiff-sqlx/src/postgres/interval.rs index 6686aac6..3fbbf8a8 100644 --- a/jiff-sqlx/src/postgres/interval.rs +++ b/jiff-sqlx/src/postgres/interval.rs @@ -3,7 +3,7 @@ use sqlx::encode::IsNull; use sqlx::error::BoxDynError; use sqlx::postgres::types::{Oid, PgInterval}; use sqlx::postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo}; -use sqlx::{Encode, Postgres, Type}; +use sqlx::{Database, Decode, Encode, Postgres, Type}; impl Type for SignedDuration { fn type_info() -> PgTypeInfo { @@ -59,6 +59,16 @@ impl Encode<'_, Postgres> for SignedDuration { } } +impl<'r> Decode<'r, Postgres> for SignedDuration { + fn decode( + value: ::ValueRef<'r>, + ) -> Result { + let pg_interval = PgInterval::decode(value)?; + let micros = pg_interval.microseconds; + Ok(SignedDuration(jiff::SignedDuration::from_micros(micros))) + } +} + #[cfg(test)] mod tests { use crate::ToSignedDuration; From 9d4000c5747583e34cf2c32217e5a709db59be09 Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 5 Jan 2025 08:33:43 +0800 Subject: [PATCH 14/15] Revert "impl Decode for SignedDuration" This reverts commit 2f17955624151a5327365c0d5d7bf597cf15cacd. --- jiff-sqlx/src/postgres/interval.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/jiff-sqlx/src/postgres/interval.rs b/jiff-sqlx/src/postgres/interval.rs index 3fbbf8a8..6686aac6 100644 --- a/jiff-sqlx/src/postgres/interval.rs +++ b/jiff-sqlx/src/postgres/interval.rs @@ -3,7 +3,7 @@ use sqlx::encode::IsNull; use sqlx::error::BoxDynError; use sqlx::postgres::types::{Oid, PgInterval}; use sqlx::postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo}; -use sqlx::{Database, Decode, Encode, Postgres, Type}; +use sqlx::{Encode, Postgres, Type}; impl Type for SignedDuration { fn type_info() -> PgTypeInfo { @@ -59,16 +59,6 @@ impl Encode<'_, Postgres> for SignedDuration { } } -impl<'r> Decode<'r, Postgres> for SignedDuration { - fn decode( - value: ::ValueRef<'r>, - ) -> Result { - let pg_interval = PgInterval::decode(value)?; - let micros = pg_interval.microseconds; - Ok(SignedDuration(jiff::SignedDuration::from_micros(micros))) - } -} - #[cfg(test)] mod tests { use crate::ToSignedDuration; From 082b29c79e16c093d0ec796b3231e2504ffbe05f Mon Sep 17 00:00:00 2001 From: tison Date: Sat, 25 Jan 2025 19:52:42 +0800 Subject: [PATCH 15/15] impl Decode for SignedDuration Signed-off-by: tison --- jiff-sqlx/src/postgres/interval.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/jiff-sqlx/src/postgres/interval.rs b/jiff-sqlx/src/postgres/interval.rs index 6686aac6..51f89cc1 100644 --- a/jiff-sqlx/src/postgres/interval.rs +++ b/jiff-sqlx/src/postgres/interval.rs @@ -3,7 +3,7 @@ use sqlx::encode::IsNull; use sqlx::error::BoxDynError; use sqlx::postgres::types::{Oid, PgInterval}; use sqlx::postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo}; -use sqlx::{Encode, Postgres, Type}; +use sqlx::{Database, Decode, Encode, Postgres, Type}; impl Type for SignedDuration { fn type_info() -> PgTypeInfo { @@ -59,6 +59,29 @@ impl Encode<'_, Postgres> for SignedDuration { } } +impl<'r> Decode<'r, Postgres> for SignedDuration { + fn decode( + value: ::ValueRef<'r>, + ) -> Result { + let pg_interval = PgInterval::decode(value)?; + + if pg_interval.months != 0 { + return Err( + "Cannot convert months in `INTERVAL` to SignedDuration".into(), + ); + } + + if pg_interval.days != 0 { + return Err( + "Cannot convert days in `INTERVAL` to SignedDuration".into() + ); + } + + let micros = pg_interval.microseconds; + Ok(SignedDuration(jiff::SignedDuration::from_micros(micros))) + } +} + #[cfg(test)] mod tests { use crate::ToSignedDuration;