From 0060346f418b554adb27bcd22003c05bfb40dfe0 Mon Sep 17 00:00:00 2001 From: vados Date: Sun, 10 Mar 2024 21:55:27 +0900 Subject: [PATCH] feat(pgrx): add chrono feature for easy conversions This commit adds a `chrono` feature flag to `pgrx`, which enables conversions between `pgrx` native date/time (with or without timezone) and `chrono` types like `NaiveDateTime`. --- Cargo.lock | 63 +++++++++++++++++++++++ Cargo.toml | 1 + pgrx-tests/Cargo.toml | 2 + pgrx-tests/src/tests/chrono_tests.rs | 32 ++++++++++++ pgrx-tests/src/tests/mod.rs | 2 + pgrx/Cargo.toml | 2 + pgrx/src/datum/datetime_support/chrono.rs | 19 +++++++ pgrx/src/datum/datetime_support/mod.rs | 3 ++ 8 files changed, 124 insertions(+) create mode 100644 pgrx-tests/src/tests/chrono_tests.rs create mode 100644 pgrx/src/datum/datetime_support/chrono.rs diff --git a/Cargo.lock b/Cargo.lock index e3e4b0e3a2..1a54ac1750 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.11" @@ -417,6 +432,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.0", +] + [[package]] name = "clang-sys" version = "1.7.0" @@ -984,6 +1013,29 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.5.0" @@ -1430,6 +1482,7 @@ dependencies = [ "atomic-traits", "bitflags 2.4.2", "bitvec", + "chrono", "enum-map", "heapless", "libc", @@ -1515,6 +1568,7 @@ dependencies = [ name = "pgrx-tests" version = "0.12.0-alpha.1" dependencies = [ + "chrono", "clap-cargo", "eyre", "libc", @@ -2895,6 +2949,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 02085b7719..6f6cf1a722 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ thiserror = "1" unescape = "0.1.0" # for escaped-character-handling url = "2.4.1" # the non-existent std::web walkdir = "2" # directory recursion +chrono = "0.4.35" # conversions to chrono data structures [profile.dev] # Only include line tables in debuginfo. This reduces the size of target/ (after diff --git a/pgrx-tests/Cargo.toml b/pgrx-tests/Cargo.toml index b8f78abb75..84dae6134c 100644 --- a/pgrx-tests/Cargo.toml +++ b/pgrx-tests/Cargo.toml @@ -38,6 +38,7 @@ pg_test = [ ] proptest = [ "dep:proptest" ] cshim = [ "pgrx/cshim" ] no-schema-generation = [ "pgrx/no-schema-generation", "pgrx-macros/no-schema-generation" ] +chrono = [ "dep:chrono", "pgrx/chrono" ] [package.metadata.docs.rs] features = ["pg14", "proptest"] @@ -64,6 +65,7 @@ serde = "1.0" serde_json = "1.0" sysinfo = "0.29.10" rand = "0.8.5" +chrono = { workspace = true, optional = true } [dependencies.pgrx] # Not unified in workspace due to default-features key path = "../pgrx" diff --git a/pgrx-tests/src/tests/chrono_tests.rs b/pgrx-tests/src/tests/chrono_tests.rs new file mode 100644 index 0000000000..8dd6942d36 --- /dev/null +++ b/pgrx-tests/src/tests/chrono_tests.rs @@ -0,0 +1,32 @@ +#![cfg(feature = "chrono")] +use pgrx::prelude::*; + +#[cfg(any(test, feature = "pg_test"))] +#[pgrx::pg_schema] +mod tests { + #[allow(unused_imports)] + use crate as pgrx_tests; + + use std::result::Result; + + use pgrx::datum::Date as PgrxDate; + use pgrx::pg_test; + use pgrx::DateTimeConversionError; + + use chrono::Datelike as _; + use chrono::NaiveDate as ChronoNaiveDate; + + // Utility class for errors + type DateTimeConversionResult = Result; + + /// Ensure simple conversion ([`pgrx::Date`] -> [`chrono::NaiveDate`]) works + #[pg_test] + fn chrono_simple_date_conversion() -> DateTimeConversionResult<()> { + let original = PgrxDate::new(1970, 1, 1)?; + let d = ChronoNaiveDate::try_from(original)?; + assert_eq!(d.year(), original.year()); + assert_eq!(d.month(), 1); + assert_eq!(d.day(), 1); + Ok(()) + } +} diff --git a/pgrx-tests/src/tests/mod.rs b/pgrx-tests/src/tests/mod.rs index f433f217c2..b3c12671b8 100644 --- a/pgrx-tests/src/tests/mod.rs +++ b/pgrx-tests/src/tests/mod.rs @@ -14,6 +14,8 @@ mod attributes_tests; mod bgworker_tests; mod bytea_tests; mod cfg_tests; +#[cfg(feature = "chrono")] +mod chrono_tests; mod composite_type_tests; mod datetime_tests; mod default_arg_value_tests; diff --git a/pgrx/Cargo.toml b/pgrx/Cargo.toml index 8a14757bbb..55451edfff 100644 --- a/pgrx/Cargo.toml +++ b/pgrx/Cargo.toml @@ -35,6 +35,7 @@ pg15 = [ "pgrx-pg-sys/pg15" ] pg16 = [ "pgrx-pg-sys/pg16" ] no-schema-generation = ["pgrx-macros/no-schema-generation", "pgrx-sql-entity-graph/no-schema-generation"] unsafe-postgres = [] # when trying to compile against something that looks like Postgres but claims to be diffent +chrono = [ "dep:chrono" ] [package.metadata.docs.rs] features = ["pg14", "cshim"] @@ -59,6 +60,7 @@ enum-map = "2.6.3" atomic-traits = "0.3.0" # PgAtomic and shmem init bitflags = "2.4.0" # BackgroundWorker bitvec = "1.0" # processing array nullbitmaps +chrono = { workspace = true, optional = true } # Conversions to chrono date time types heapless = "0.8" # shmem and PgLwLock libc.workspace = true # FFI type compat seahash = "4.1.0" # derive(PostgresHash) diff --git a/pgrx/src/datum/datetime_support/chrono.rs b/pgrx/src/datum/datetime_support/chrono.rs new file mode 100644 index 0000000000..ab90e2d30e --- /dev/null +++ b/pgrx/src/datum/datetime_support/chrono.rs @@ -0,0 +1,19 @@ +//! This module contains implementations and functionality that enables [`pgrx`] types (ex. [`pgrx::datum::Date`]) +//! to be converted to [`chrono`] data types (ex. [`chrono::Date`]) +#![cfg(feature = "chrono")] + +use std::convert::TryFrom; + +use chrono; + +use crate::datum::datetime_support::DateTimeConversionError; +use crate::datum::Date; + +impl TryFrom for chrono::naive::NaiveDate { + type Error = DateTimeConversionError; + + fn try_from(d: Date) -> Result { + chrono::NaiveDate::from_ymd_opt(d.year(), d.month().into(), d.day().into()) + .ok_or_else(|| DateTimeConversionError::InvalidFormat) + } +} diff --git a/pgrx/src/datum/datetime_support/mod.rs b/pgrx/src/datum/datetime_support/mod.rs index 69f90b7e6e..f7484c7e73 100644 --- a/pgrx/src/datum/datetime_support/mod.rs +++ b/pgrx/src/datum/datetime_support/mod.rs @@ -22,6 +22,9 @@ use std::marker::PhantomData; mod ctor; mod ops; +#[cfg(feature = "chrono")] +mod chrono; + pub use ctor::*; /// Tags to identify which "part" of a date or time-type value to extract or truncate to