diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 297beddb1b..86a55be3cb 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -155,10 +155,10 @@ jobs: kind: build - label: tokio native-tls mysql kind: test - features: sqlx-mysql,runtime-tokio-native-tls + features: sqlx-mysql,runtime-tokio-native-tls,unimplemented-jiff-sqlx-mysql - label: tokio rustls mysql kind: test - features: sqlx-mysql,runtime-tokio-rustls + features: sqlx-mysql,runtime-tokio-rustls,unimplemented-jiff-sqlx-mysql - label: tokio native-tls postgres kind: test features: sqlx-postgres,runtime-tokio-native-tls @@ -367,8 +367,10 @@ jobs: - working-directory: ./sea-orm-sync run: cargo test --test '*' --features tests-features,rusqlite - working-directory: ./sea-orm-sync/examples/quickstart + name: run quickstart example run: cargo run - working-directory: ./sea-orm-sync/examples/parquet_example + name: run parquet example run: cargo run mysql: @@ -417,8 +419,8 @@ jobs: ~/.cargo/git/db/ key: ${{ runner.os }}-cargo-mysql-tests-${{ hashFiles('**/Cargo.toml') }} - uses: mozilla-actions/sccache-action@v0.0.9 - - run: cargo test --test '*' --features tests-features,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }} --no-run - - run: cargo test --test '*' --features tests-features,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + - run: cargo test --test '*' --features tests-features,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }},unimplemented-jiff-sqlx-mysql --no-run + - run: cargo test --test '*' --features tests-features,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }},unimplemented-jiff-sqlx-mysql - run: cargo test --manifest-path sea-orm-migration/Cargo.toml --test '*' --features sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }} --no-run - run: cargo test --manifest-path sea-orm-migration/Cargo.toml --test '*' --features sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }} @@ -470,8 +472,8 @@ jobs: ~/.cargo/git/db/ key: ${{ runner.os }}-cargo-mariadb-tests-${{ hashFiles('**/Cargo.toml') }} - uses: mozilla-actions/sccache-action@v0.0.9 - - run: cargo test --test '*' --features tests-features,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }} --no-run - - run: cargo test --test '*' --features tests-features,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }} + - run: cargo test --test '*' --features tests-features,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }},unimplemented-jiff-sqlx-mysql --no-run + - run: cargo test --test '*' --features tests-features,sqlx-mysql,runtime-${{ matrix.runtime }}-${{ matrix.tls }},unimplemented-jiff-sqlx-mysql postgres: name: Postgres diff --git a/Cargo.toml b/Cargo.toml index 6d090973e0..8a6ba87128 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,11 @@ futures-util = { version = "0.3", default-features = false, features = [ inventory = { version = "0.3", optional = true } ipnetwork = { version = "0.20", default-features = false, optional = true } itertools = "0.14.0" +jiff = { version = "0.2.15", default-features = false, optional = true, features = [ + "std", + "perf-inline", +] } +jiff-sqlx = { version = "0.1.1", default-features = false, optional = true } log = { version = "0.4", default-features = false } mac_address = { version = "1.1", default-features = false, optional = true } ouroboros = { version = "0.18", default-features = false } @@ -98,6 +103,7 @@ sea-orm = { path = ".", features = [ "mock", "postgres-array", "tests-cfg", + "with-jiff", ] } smol = { version = "1.2" } smol-potat = { version = "1.1" } @@ -165,12 +171,14 @@ sqlx-mysql = [ sqlx-postgres = [ "sqlx-dep", "sea-query-sqlx/sqlx-postgres", + "jiff-sqlx?/postgres", "postgres-array", "sea-schema?/sqlx-postgres", ] sqlx-sqlite = [ "sqlx-dep", "sea-query-sqlx/sqlx-sqlite", + "jiff-sqlx?/sqlite", "sea-schema?/sqlx-sqlite", ] sync = [] @@ -182,9 +190,13 @@ tests-features = [ "schema-sync", "with-arrow", "with-bigdecimal", + "with-jiff", "with-ipnetwork", ] tracing-spans = [] +unimplemented-jiff-sqlx-mysql = [ + "sea-query-sqlx?/unimplemented-jiff-sqlx-mysql", +] with-arrow = ["sea-orm-arrow", "sea-orm-macros/with-arrow"] with-bigdecimal = [ "bigdecimal", @@ -203,11 +215,18 @@ with-ipnetwork = [ "sea-query/with-ipnetwork", "sea-query-sqlx?/with-ipnetwork", ] +with-jiff = [ + "jiff", + "jiff-sqlx", + "sea-query/with-jiff", + "sea-query-sqlx?/with-jiff", +] with-json = [ "serde_json", "sea-query/with-json", "sea-orm-macros/with-json", "chrono?/serde", + "jiff?/serde", "rust_decimal?/serde", "bigdecimal?/serde", "uuid?/serde", @@ -237,5 +256,6 @@ with-uuid = ["uuid", "sea-query/with-uuid", "sea-query-sqlx?/with-uuid"] # This allows us to develop using a local version of sea-query [patch.crates-io] -# sea-query = { path = "../sea-query" } +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } # sea-query = { git = "https://github.com/SeaQL/sea-query", branch = "master" } diff --git a/examples/actix_example/Cargo.toml b/examples/actix_example/Cargo.toml index 3fb8a62b61..62dc5bdd53 100644 --- a/examples/actix_example/Cargo.toml +++ b/examples/actix_example/Cargo.toml @@ -11,3 +11,7 @@ members = [".", "api", "entity", "migration"] [dependencies] actix-example-api = { path = "api" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/axum_example/Cargo.toml b/examples/axum_example/Cargo.toml index e7781d9abb..27bd607a36 100644 --- a/examples/axum_example/Cargo.toml +++ b/examples/axum_example/Cargo.toml @@ -11,3 +11,7 @@ members = [".", "api", "entity", "migration"] [dependencies] axum-example-api = { path = "api" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/basic/Cargo.toml b/examples/basic/Cargo.toml index 8bd8f1a779..7406fd20cc 100644 --- a/examples/basic/Cargo.toml +++ b/examples/basic/Cargo.toml @@ -16,3 +16,7 @@ sea-orm = { path = "../../", features = [ ] } serde_json = { version = "1" } tokio = { version = "1", features = ["full"] } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/graphql_example/Cargo.toml b/examples/graphql_example/Cargo.toml index b2a1bd2fed..22a956bf23 100644 --- a/examples/graphql_example/Cargo.toml +++ b/examples/graphql_example/Cargo.toml @@ -12,3 +12,7 @@ members = [".", "api", "entity", "migration"] [dependencies] graphql-example-api = { path = "api" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/jsonrpsee_example/Cargo.toml b/examples/jsonrpsee_example/Cargo.toml index 4c7e717ad4..339b21df31 100644 --- a/examples/jsonrpsee_example/Cargo.toml +++ b/examples/jsonrpsee_example/Cargo.toml @@ -11,3 +11,7 @@ members = [".", "api", "entity", "migration"] [dependencies] jsonrpsee-example-api = { path = "api" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/parquet_example/Cargo.toml b/examples/parquet_example/Cargo.toml index a17e0d93f2..c40186470a 100644 --- a/examples/parquet_example/Cargo.toml +++ b/examples/parquet_example/Cargo.toml @@ -24,3 +24,7 @@ features = [ "with-rust_decimal", ] path = "../../" + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/poem_example/Cargo.toml b/examples/poem_example/Cargo.toml index eb124fb2b1..c7608656c7 100644 --- a/examples/poem_example/Cargo.toml +++ b/examples/poem_example/Cargo.toml @@ -10,3 +10,7 @@ members = [".", "api", "entity", "migration"] [dependencies] poem-example-api = { path = "api" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/proxy_gluesql_example/Cargo.toml b/examples/proxy_gluesql_example/Cargo.toml index fbd68b9cd1..496a6e6360 100644 --- a/examples/proxy_gluesql_example/Cargo.toml +++ b/examples/proxy_gluesql_example/Cargo.toml @@ -31,3 +31,7 @@ sqlparser = "0.40" [dev-dependencies] smol = { version = "1.2" } smol-potat = { version = "1.1" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/quickstart/Cargo.toml b/examples/quickstart/Cargo.toml index ea3dfba255..379e9a99c4 100644 --- a/examples/quickstart/Cargo.toml +++ b/examples/quickstart/Cargo.toml @@ -22,3 +22,7 @@ features = [ "schema-sync", ] path = "../../" + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/rocket_example/Cargo.toml b/examples/rocket_example/Cargo.toml index 119c09a629..568f07294e 100644 --- a/examples/rocket_example/Cargo.toml +++ b/examples/rocket_example/Cargo.toml @@ -11,3 +11,7 @@ members = [".", "api", "entity", "migration"] [dependencies] rocket-example-api = { path = "api" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/rocket_okapi_example/Cargo.toml b/examples/rocket_okapi_example/Cargo.toml index c8aa9f321d..f3bfc5e0d6 100644 --- a/examples/rocket_okapi_example/Cargo.toml +++ b/examples/rocket_okapi_example/Cargo.toml @@ -14,3 +14,7 @@ members = [".", "api", "service", "entity", "migration", "dto"] [dependencies] rocket-example-api = { path = "api" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/salvo_example/Cargo.toml b/examples/salvo_example/Cargo.toml index befaf17bb2..dee44d4b66 100644 --- a/examples/salvo_example/Cargo.toml +++ b/examples/salvo_example/Cargo.toml @@ -10,3 +10,7 @@ members = [".", "api", "entity", "migration"] [dependencies] salvo-example-api = { path = "api" } + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/seaography_example/migration/Cargo.toml b/examples/seaography_example/migration/Cargo.toml index 6b1574c923..39f656b6ef 100644 --- a/examples/seaography_example/migration/Cargo.toml +++ b/examples/seaography_example/migration/Cargo.toml @@ -25,3 +25,7 @@ features = [ ] path = "../../../sea-orm-migration" # remove this line in your own project version = "~2.0.0-rc.37" # sea-orm-migration version + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/examples/tonic_example/Cargo.toml b/examples/tonic_example/Cargo.toml index f2233dac1d..8e122bdb60 100644 --- a/examples/tonic_example/Cargo.toml +++ b/examples/tonic_example/Cargo.toml @@ -22,3 +22,7 @@ path = "./src/server.rs" [[bin]] name = "client" path = "./src/client.rs" + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/sea-orm-cli/Cargo.toml b/sea-orm-cli/Cargo.toml index c1394cbcf8..e92c40ac12 100644 --- a/sea-orm-cli/Cargo.toml +++ b/sea-orm-cli/Cargo.toml @@ -128,5 +128,6 @@ runtime-tokio-rustls = [ # This allows us to develop using an overridden version of sea-query [patch.crates-io] -# sea-query = { path = "../sea-query" } +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } # sea-query = { git = "https://github.com/SeaQL/sea-query", branch = "master" } diff --git a/sea-orm-cli/src/cli.rs b/sea-orm-cli/src/cli.rs index 810a81ba66..30d1ce55f3 100644 --- a/sea-orm-cli/src/cli.rs +++ b/sea-orm-cli/src/cli.rs @@ -399,6 +399,7 @@ pub enum DateTimeCrate { #[default] Chrono, Time, + Jiff, } #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum, Default)] diff --git a/sea-orm-cli/src/commands/generate.rs b/sea-orm-cli/src/commands/generate.rs index 7e9c5916e2..053cb9ebbe 100644 --- a/sea-orm-cli/src/commands/generate.rs +++ b/sea-orm-cli/src/commands/generate.rs @@ -360,6 +360,7 @@ impl From for CodegenDateTimeCrate { match date_time_crate { DateTimeCrate::Chrono => CodegenDateTimeCrate::Chrono, DateTimeCrate::Time => CodegenDateTimeCrate::Time, + DateTimeCrate::Jiff => CodegenDateTimeCrate::Jiff, } } } diff --git a/sea-orm-codegen/src/entity/column.rs b/sea-orm-codegen/src/entity/column.rs index 25f650fee7..9a87c42004 100644 --- a/sea-orm-codegen/src/entity/column.rs +++ b/sea-orm-codegen/src/entity/column.rs @@ -60,22 +60,27 @@ impl Column { ColumnType::Date => match opt.date_time_crate { DateTimeCrate::Chrono => "Date".to_owned(), DateTimeCrate::Time => "TimeDate".to_owned(), + DateTimeCrate::Jiff => "JiffDate".to_owned(), }, ColumnType::Time => match opt.date_time_crate { DateTimeCrate::Chrono => "Time".to_owned(), DateTimeCrate::Time => "TimeTime".to_owned(), + DateTimeCrate::Jiff => "JiffTime".to_owned(), }, ColumnType::DateTime => match opt.date_time_crate { DateTimeCrate::Chrono => "DateTime".to_owned(), DateTimeCrate::Time => "TimeDateTime".to_owned(), + DateTimeCrate::Jiff => "JiffDateTime".to_owned(), }, ColumnType::Timestamp => match opt.date_time_crate { DateTimeCrate::Chrono => "DateTimeUtc".to_owned(), DateTimeCrate::Time => "TimeDateTime".to_owned(), + DateTimeCrate::Jiff => "JiffDateTime".to_owned(), }, ColumnType::TimestampWithTimeZone => match opt.date_time_crate { DateTimeCrate::Chrono => "DateTimeWithTimeZone".to_owned(), DateTimeCrate::Time => "TimeDateTimeWithTimeZone".to_owned(), + DateTimeCrate::Jiff => "JiffTimestamp".to_owned(), }, ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal".to_owned(), ColumnType::Uuid => "Uuid".to_owned(), @@ -107,6 +112,7 @@ impl Column { pub fn get_col_type_attrs(&self) -> Option { let col_type = match &self.col_type { + ColumnType::DateTime => Some("DateTime".to_owned()), ColumnType::Float => Some("Float".to_owned()), ColumnType::Double => Some("Double".to_owned()), ColumnType::Decimal(Some((p, s))) => Some(format!("Decimal(Some(({p}, {s})))")), @@ -329,6 +335,13 @@ mod tests { } } + fn date_time_crate_jiff() -> ColumnOption { + ColumnOption { + date_time_crate: DateTimeCrate::Jiff, + big_integer_type: Default::default(), + } + } + fn setup() -> Vec { macro_rules! make_col { ($name:expr, $col_type:expr) => { @@ -526,6 +539,48 @@ mod tests { quote!(Option<#rs_type>).to_string() ); } + + let columns = setup(); + let rs_types = vec![ + "String", + "String", + "String", + "i8", + "u8", + "i16", + "u16", + "i32", + "u32", + "i64", + "u64", + "f32", + "f64", + "Vec", + "Vec", + "Vec", + "Vec", + "bool", + "JiffDate", + "JiffTime", + "JiffDateTime", + "JiffDateTime", + "JiffTimestamp", + ]; + for (mut col, rs_type) in columns.into_iter().zip(rs_types) { + let rs_type: TokenStream = rs_type.parse().unwrap(); + + col.not_null = true; + assert_eq!( + col.get_rs_type(&date_time_crate_jiff()).to_string(), + quote!(#rs_type).to_string() + ); + + col.not_null = false; + assert_eq!( + col.get_rs_type(&date_time_crate_jiff()).to_string(), + quote!(Option<#rs_type>).to_string() + ); + } } #[test] @@ -712,6 +767,55 @@ mod tests { column.get_info(&date_time_crate_time()).as_str(), "Column `timestamp_with_timezone_field`: TimeDateTimeWithTimeZone, not_null" ); + let column: Column = ColumnDef::new(Alias::new("date_field")) + .date() + .not_null() + .to_owned() + .into(); + assert_eq!( + column.get_info(&date_time_crate_jiff()).as_str(), + "Column `date_field`: JiffDate, not_null" + ); + + let column: Column = ColumnDef::new(Alias::new("time_field")) + .time() + .not_null() + .to_owned() + .into(); + assert_eq!( + column.get_info(&date_time_crate_jiff()).as_str(), + "Column `time_field`: JiffTime, not_null" + ); + + let column: Column = ColumnDef::new(Alias::new("date_time_field")) + .date_time() + .not_null() + .to_owned() + .into(); + assert_eq!( + column.get_info(&date_time_crate_jiff()).as_str(), + "Column `date_time_field`: JiffDateTime, not_null" + ); + + let column: Column = ColumnDef::new(Alias::new("timestamp_field")) + .timestamp() + .not_null() + .to_owned() + .into(); + assert_eq!( + column.get_info(&date_time_crate_jiff()).as_str(), + "Column `timestamp_field`: JiffDateTime, not_null" + ); + + let column: Column = ColumnDef::new(Alias::new("timestamp_with_timezone_field")) + .timestamp_with_time_zone() + .not_null() + .to_owned() + .into(); + assert_eq!( + column.get_info(&date_time_crate_jiff()).as_str(), + "Column `timestamp_with_timezone_field`: JiffTimestamp, not_null" + ); } #[test] diff --git a/sea-orm-codegen/src/entity/writer.rs b/sea-orm-codegen/src/entity/writer.rs index b94b9aa5e0..47b9f68847 100644 --- a/sea-orm-codegen/src/entity/writer.rs +++ b/sea-orm-codegen/src/entity/writer.rs @@ -49,6 +49,7 @@ pub enum DateTimeCrate { #[default] Chrono, Time, + Jiff, } #[derive(Debug, Default, PartialEq, Eq, Copy, Clone)] diff --git a/sea-orm-migration/Cargo.toml b/sea-orm-migration/Cargo.toml index 2e403b65c8..aa89ced531 100644 --- a/sea-orm-migration/Cargo.toml +++ b/sea-orm-migration/Cargo.toml @@ -88,10 +88,12 @@ sqlx-sqlite = ["sqlx-dep", "sea-orm/sqlx-sqlite", "sea-orm-cli?/sqlx-sqlite"] with-bigdecimal = ["sea-orm/with-bigdecimal"] with-chrono = ["sea-orm/with-chrono"] with-ipnetwork = ["sea-orm/with-ipnetwork"] +with-jiff = ["sea-orm/with-jiff"] with-json = ["sea-orm/with-json"] with-rust_decimal = ["sea-orm/with-rust_decimal"] with-time = ["sea-orm/with-time"] with-uuid = ["sea-orm/with-uuid"] [patch.crates-io] -# sea-query = { path = "../sea-query" } +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-sqlx = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/sea-orm-sync/Cargo.toml b/sea-orm-sync/Cargo.toml index 29c8c58920..803bdb7001 100644 --- a/sea-orm-sync/Cargo.toml +++ b/sea-orm-sync/Cargo.toml @@ -24,6 +24,8 @@ features = [ "schema-sync", "postgres-array", "postgres-vector", + "tracing-spans", + "with-ipnetwork", "with-arrow", ] rustdoc-args = ["--cfg", "docsrs"] @@ -41,6 +43,11 @@ derive_more = { version = "2", features = ["debug"] } inventory = { version = "0.3", optional = true } ipnetwork = { version = "0.20", default-features = false, optional = true } itertools = "0.14.0" +jiff = { version = "0.2.15", default-features = false, optional = true, features = [ + "std", + "perf-inline", +] } +jiff-sqlx = { version = "0.1.1", default-features = false, optional = true } log = { version = "0.4", default-features = false } ouroboros = { version = "0.18", default-features = false } pgvector = { version = "~0.4", default-features = false, optional = true } @@ -48,10 +55,10 @@ rust_decimal = { version = "1", default-features = false, features = [ "std", ], optional = true } sea-orm-arrow = { version = "2.0.0-rc", path = "../sea-orm-arrow", default-features = false, optional = true } -sea-orm-macros = { version = "~2.0.0-rc.20", path = "../sea-orm-macros", default-features = false, features = [ +sea-orm-macros = { version = "~2.0.0-rc.37", path = "../sea-orm-macros", default-features = false, features = [ "strum", ] } -sea-query = { version = "=1.0.0-rc.31", default-features = false, features = [ +sea-query = { version = "=1.0.0-rc.32", default-features = false, features = [ "thread-safe", "hashable-value", "backend-mysql", @@ -68,7 +75,7 @@ sea-schema-sync = { version = "0.17.0-rc.15", default-features = false, features ], optional = true } serde = { version = "1.0", default-features = false } serde_json = { version = "1.0", default-features = false, optional = true } -strum = { version = "0.27", default-features = false } +strum = { version = "0.28", default-features = false } thiserror = { version = "2", default-features = false } time = { version = "0.3.36", default-features = false, optional = true } tracing = { version = "0.1", default-features = false, features = [ @@ -87,6 +94,7 @@ sea-orm-sync = { path = ".", features = [ "mock", "postgres-array", "tests-cfg", + "with-jiff", ] } time = { version = "0.3.36", features = ["macros"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } @@ -137,10 +145,13 @@ sync = [] tests-cfg = ["serde/derive"] tests-features = [ "default", + "postgres-array", "rbac", "schema-sync", "with-arrow", "with-bigdecimal", + "with-jiff", + "with-ipnetwork", ] tracing-spans = [] with-arrow = ["sea-orm-arrow", "sea-orm-macros/with-arrow"] @@ -161,11 +172,18 @@ with-ipnetwork = [ "sea-query/with-ipnetwork", "sea-query-rusqlite?/with-ipnetwork", ] +with-jiff = [ + "jiff", + "jiff-sqlx", + "sea-query/with-jiff", + "sea-query-rusqlite?/with-jiff", +] with-json = [ "serde_json", "sea-query/with-json", "sea-orm-macros/with-json", "chrono?/serde", + "jiff?/serde", "rust_decimal?/serde", "bigdecimal?/serde", "uuid?/serde", @@ -188,4 +206,5 @@ with-time = [ with-uuid = ["uuid", "sea-query/with-uuid", "sea-query-rusqlite?/with-uuid"] [patch.crates-io] -# sea-query = { path = "../sea-query" } +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-rusqlite = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/sea-orm-sync/examples/parquet_example/Cargo.toml b/sea-orm-sync/examples/parquet_example/Cargo.toml index 8d7b772310..3ce16f1196 100644 --- a/sea-orm-sync/examples/parquet_example/Cargo.toml +++ b/sea-orm-sync/examples/parquet_example/Cargo.toml @@ -22,3 +22,7 @@ features = [ "with-rust_decimal", ] path = "../../" + +[patch.crates-io] +sea-query = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } +sea-query-rusqlite = { git = "https://github.com/Huliiiiii/sea-query", branch = "jiff" } diff --git a/sea-orm-sync/src/driver/sqlx_postgres.rs b/sea-orm-sync/src/driver/sqlx_postgres.rs index fe55712a68..8adf020a8b 100644 --- a/sea-orm-sync/src/driver/sqlx_postgres.rs +++ b/sea-orm-sync/src/driver/sqlx_postgres.rs @@ -674,216 +674,411 @@ pub(crate) fn from_sqlx_postgres_row_to_proxy_row(row: &sqlx::postgres::PgRow) - }), ), - #[cfg(feature = "with-chrono")] - "TIMESTAMP" => Value::ChronoDateTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timestamp"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "TIMESTAMP" => Value::TimeDateTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timestamp"), - ), + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] + "TIMESTAMP" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoDateTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeDateTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp"), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::JiffDateTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp") + .map(Into::into) + .map(Box::new), + ); + }; + value + } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "TIMESTAMP[]" => Value::Array( - sea_query::ArrayType::ChronoDateTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamp array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoDateTime(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( - feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "postgres-array", + any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ) ))] - "TIMESTAMP[]" => Value::Array( - sea_query::ArrayType::TimeDateTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamp array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeDateTime(Some(val))) - .collect(), + "TIMESTAMP[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoDateTime, + row.try_get::>, _>( + c.ordinal(), ) - }), - ), + .expect("Failed to get timestamp array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoDateTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeDateTime, + row.try_get::>, _>( + c.ordinal(), + ) + .expect("Failed to get timestamp array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::TimeDateTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::Array( + sea_query::ArrayType::JiffDateTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timestamp array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(Into::::into) + .map(|val| { + Value::JiffDateTime(Some(Box::new(val))) + }) + .collect(), + ) + }), + ); + }; + value + } - #[cfg(feature = "with-chrono")] - "DATE" => Value::ChronoDate( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get date"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "DATE" => Value::TimeDate( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get date"), - ), + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] + "DATE" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date"), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::JiffDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date") + .map(Into::into), + ); + }; + value + } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "DATE[]" => Value::Array( - sea_query::ArrayType::ChronoDate, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get date array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoDate(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( - feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "postgres-array", + any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ) ))] - "DATE[]" => Value::Array( - sea_query::ArrayType::TimeDate, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get date array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeDate(Some(val))) - .collect(), - ) - }), - ), + "DATE[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoDate, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get date array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoDate(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeDate, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get date array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::TimeDate(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::Array( + sea_query::ArrayType::JiffDate, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get date array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(Into::::into) + .map(|val| Value::JiffDate(Some(val))) + .collect(), + ) + }), + ); + }; + value + } - #[cfg(feature = "with-chrono")] - "TIME" => Value::ChronoTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get time"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "TIME" => Value::TimeTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get time"), - ), + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] + "TIME" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time"), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::JiffTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time") + .map(Into::into), + ); + }; + value + } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "TIME[]" => Value::Array( - sea_query::ArrayType::ChronoTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get time array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoTime(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( + feature = "postgres-array", + any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ) + ))] + "TIME[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get time array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get time array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::TimeTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::Array( + sea_query::ArrayType::JiffTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get time array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(Into::::into) + .map(|val| Value::JiffTime(Some(val))) + .collect(), + ) + }), + ); + }; + value + } + + #[cfg(any( + feature = "with-chrono", feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "with-jiff" ))] - "TIME[]" => Value::Array( - sea_query::ArrayType::TimeTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get time array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeTime(Some(val))) - .collect(), + "TIMESTAMPTZ" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoDateTimeUtc( + row.try_get::>, _>( + c.ordinal(), ) - }), - ), - - #[cfg(feature = "with-chrono")] - "TIMESTAMPTZ" => Value::ChronoDateTimeUtc( - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamptz"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "TIMESTAMPTZ" => Value::TimeDateTimeWithTimeZone( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timestamptz"), - ), + .expect("Failed to get timestamptz"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeDateTimeWithTimeZone( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamptz"), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::JiffTimestamp( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamptz") + .map(Into::into) + .map(Box::new), + ); + }; + value + } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "TIMESTAMPTZ[]" => Value::Array( - sea_query::ArrayType::ChronoDateTimeUtc, - row.try_get::>>, _>( - c.ordinal(), - ) - .expect("Failed to get timestamptz array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoDateTimeUtc(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( - feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "postgres-array", + any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ) ))] - "TIMESTAMPTZ[]" => Value::Array( - sea_query::ArrayType::TimeDateTimeWithTimeZone, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamptz array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeDateTimeWithTimeZone(Some(val))) - .collect(), + "TIMESTAMPTZ[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoDateTimeUtc, + row.try_get::>>, _>( + c.ordinal(), ) - }), - ), + .expect("Failed to get timestamptz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoDateTimeUtc(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeDateTimeWithTimeZone, + row.try_get::>, _>( + c.ordinal(), + ) + .expect("Failed to get timestamptz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| { + Value::TimeDateTimeWithTimeZone(Some(val)) + }) + .collect(), + ) + }), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::Array( + sea_query::ArrayType::JiffTimestamp, + row.try_get::>, _>( + c.ordinal(), + ) + .expect("Failed to get timestamptz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(Into::::into) + .map(|val| { + Value::JiffTimestamp(Some(Box::new(val))) + }) + .collect(), + ) + }), + ); + }; + value + } - #[cfg(feature = "with-chrono")] - "TIMETZ" => Value::ChronoTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timetz"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + #[cfg(any(feature = "with-chrono", feature = "with-time"))] "TIMETZ" => { - Value::TimeTime(row.try_get(c.ordinal()).expect("Failed to get timetz")) + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timetz"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeTime( + row.try_get(c.ordinal()).expect("Failed to get timetz"), + ); + }; + value } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "TIMETZ[]" => Value::Array( - sea_query::ArrayType::ChronoTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timetz array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoTime(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( - feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "postgres-array", + any(feature = "with-chrono", feature = "with-time") ))] - "TIMETZ[]" => Value::Array( - sea_query::ArrayType::TimeTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timetz array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeTime(Some(val))) - .collect(), - ) - }), - ), + "TIMETZ[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timetz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timetz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::TimeTime(Some(val))) + .collect(), + ) + }), + ); + }; + value + } #[cfg(feature = "with-uuid")] "UUID" => Value::Uuid( diff --git a/sea-orm-sync/src/driver/sqlx_sqlite.rs b/sea-orm-sync/src/driver/sqlx_sqlite.rs index 8d9456fddc..210dfc23a6 100644 --- a/sea-orm-sync/src/driver/sqlx_sqlite.rs +++ b/sea-orm-sync/src/driver/sqlx_sqlite.rs @@ -412,61 +412,112 @@ pub(crate) fn from_sqlx_sqlite_row_to_proxy_row(row: &sqlx::sqlite::SqliteRow) - .map(Box::new), ), - #[cfg(feature = "with-chrono")] + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] "DATETIME" => { - use chrono::{DateTime, Utc}; - - Value::ChronoDateTimeUtc( - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamp") - .map(Box::new), - ) - } - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "DATETIME" => { - use time::OffsetDateTime; - Value::TimeDateTimeWithTimeZone( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timestamp") - .map(Box::new), - ) + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + { + use chrono::{DateTime, Utc}; + + break 'value Value::ChronoDateTimeUtc( + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timestamp"), + ); + } + #[cfg(feature = "with-time")] + { + use time::OffsetDateTime; + + break 'value Value::TimeDateTimeWithTimeZone( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp"), + ); + } + #[cfg(feature = "with-jiff")] + break 'value Value::JiffDateTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp") + .map(Into::into) + .map(Box::new), + ); + }; + value } - #[cfg(feature = "with-chrono")] + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] "DATE" => { - use chrono::NaiveDate; - Value::ChronoDate( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get date") - .map(Box::new), - ) - } - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "DATE" => { - use time::Date; - Value::TimeDate( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get date") - .map(Box::new), - ) + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + { + use chrono::NaiveDate; + + break 'value Value::ChronoDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date"), + ); + } + #[cfg(feature = "with-time")] + { + use time::Date; + + break 'value Value::TimeDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date"), + ); + } + #[cfg(feature = "with-jiff")] + break 'value Value::JiffDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date") + .map(Into::into), + ); + }; + value } - #[cfg(feature = "with-chrono")] - "TIME" => { - use chrono::NaiveTime; - Value::ChronoTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get time") - .map(Box::new), - ) - } - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] "TIME" => { - use time::Time; - Value::TimeTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get time") - .map(Box::new), - ) + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + { + use chrono::NaiveTime; + + break 'value Value::ChronoTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time"), + ); + } + #[cfg(feature = "with-time")] + { + use time::Time; + + break 'value Value::TimeTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time"), + ); + } + #[cfg(feature = "with-jiff")] + break 'value Value::JiffTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time") + .map(Into::into), + ); + }; + value } _ => unreachable!("Unknown column type: {}", c.type_info().name()), diff --git a/sea-orm-sync/src/dynamic/model.rs b/sea-orm-sync/src/dynamic/model.rs index fe3fe30518..406eafd7d7 100644 --- a/sea-orm-sync/src/dynamic/model.rs +++ b/sea-orm-sync/src/dynamic/model.rs @@ -84,6 +84,7 @@ fn try_get(res: &QueryResult, pre: &str, col: &str, ty: &ArrayType) -> Result Value::String(res.try_get(pre, col)?), ArrayType::Char => return Err(DbErr::Type("Unsupported type: char".into())), ArrayType::Bytes => Value::Bytes(res.try_get(pre, col)?), + ArrayType::Enum(_) => Value::String(res.try_get(pre, col)?), #[cfg(feature = "with-json")] ArrayType::Json => Value::Json(res.try_get::>(pre, col)?.map(Box::new)), @@ -122,6 +123,24 @@ fn try_get(res: &QueryResult, pre: &str, col: &str, ty: &ArrayType) -> Result Value::JiffDate(res.try_get(pre, col)?), + + #[cfg(feature = "with-jiff")] + ArrayType::JiffTime => Value::JiffTime(res.try_get(pre, col)?), + + #[cfg(feature = "with-jiff")] + ArrayType::JiffDateTime => Value::JiffDateTime( + res.try_get::>(pre, col)? + .map(Box::new), + ), + + #[cfg(feature = "with-jiff")] + ArrayType::JiffTimestamp => Value::JiffTimestamp( + res.try_get::>(pre, col)? + .map(Box::new), + ), + #[cfg(feature = "with-uuid")] ArrayType::Uuid => Value::Uuid(res.try_get(pre, col)?), diff --git a/sea-orm-sync/src/entity/active_value.rs b/sea-orm-sync/src/entity/active_value.rs index 36f4186a0a..f5920756c4 100644 --- a/sea-orm-sync/src/entity/active_value.rs +++ b/sea-orm-sync/src/entity/active_value.rs @@ -132,29 +132,33 @@ where } macro_rules! impl_into_active_value { - ($ty: ty) => { - impl IntoActiveValue<$ty> for $ty { - fn into_active_value(self) -> ActiveValue<$ty> { - Set(self) + ($( $ty: ty ),+ $(,)?) => { + $( + impl IntoActiveValue<$ty> for $ty { + fn into_active_value(self) -> ActiveValue<$ty> { + Set(self) + } } - } + )+ }; } -impl_into_active_value!(bool); -impl_into_active_value!(i8); -impl_into_active_value!(i16); -impl_into_active_value!(i32); -impl_into_active_value!(i64); -impl_into_active_value!(u8); -impl_into_active_value!(u16); -impl_into_active_value!(u32); -impl_into_active_value!(u64); -impl_into_active_value!(f32); -impl_into_active_value!(f64); -impl_into_active_value!(&'static str); -impl_into_active_value!(String); -impl_into_active_value!(Vec); +impl_into_active_value!( + bool, + i8, + i16, + i32, + i64, + u8, + u16, + u32, + u64, + f32, + f64, + &'static str, + String, + Vec, +); #[cfg(feature = "with-json")] #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] @@ -162,27 +166,14 @@ impl_into_active_value!(crate::prelude::Json); #[cfg(feature = "with-chrono")] #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::Date); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::Time); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::DateTime); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::DateTimeWithTimeZone); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::DateTimeUtc); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::DateTimeLocal); +impl_into_active_value!( + crate::prelude::Date, + crate::prelude::Time, + crate::prelude::DateTime, + crate::prelude::DateTimeWithTimeZone, + crate::prelude::DateTimeUtc, + crate::prelude::DateTimeLocal, +); #[cfg(feature = "with-rust_decimal")] #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] @@ -198,19 +189,21 @@ impl_into_active_value!(crate::prelude::Uuid); #[cfg(feature = "with-time")] #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -impl_into_active_value!(crate::prelude::TimeDate); - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -impl_into_active_value!(crate::prelude::TimeTime); - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -impl_into_active_value!(crate::prelude::TimeDateTime); - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -impl_into_active_value!(crate::prelude::TimeDateTimeWithTimeZone); +impl_into_active_value!( + crate::prelude::TimeDate, + crate::prelude::TimeTime, + crate::prelude::TimeDateTime, + crate::prelude::TimeDateTimeWithTimeZone, +); + +#[cfg(feature = "with-jiff")] +#[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] +impl_into_active_value!( + crate::prelude::JiffDate, + crate::prelude::JiffTime, + crate::prelude::JiffDateTime, + crate::prelude::JiffTimestamp, +); #[cfg(feature = "with-ipnetwork")] #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] diff --git a/sea-orm-sync/src/entity/column/types.rs b/sea-orm-sync/src/entity/column/types.rs index d0c90e2e77..6e9661ab1f 100644 --- a/sea-orm-sync/src/entity/column/types.rs +++ b/sea-orm-sync/src/entity/column/types.rs @@ -76,7 +76,7 @@ impl_expr_traits!(GenericArrayColumn); #[cfg(feature = "with-json")] mod with_json; -#[cfg(any(feature = "with-chrono", feature = "with-time"))] +#[cfg(any(feature = "with-chrono", feature = "with-time", feature = "with-jiff"))] mod with_datetime; #[cfg(feature = "with-uuid")] diff --git a/sea-orm-sync/src/entity/prelude.rs b/sea-orm-sync/src/entity/prelude.rs index 6e3e2cbc13..9ab4eef5e1 100644 --- a/sea-orm-sync/src/entity/prelude.rs +++ b/sea-orm-sync/src/entity/prelude.rs @@ -89,6 +89,21 @@ pub use time::OffsetDateTime as TimeDateTimeWithTimeZone; #[cfg(feature = "with-time")] pub use crate::value::{TimeUnixTimestamp, TimeUnixTimestampMillis}; +#[cfg(feature = "with-jiff")] +pub use jiff::Timestamp as JiffTimestamp; + +#[cfg(feature = "with-jiff")] +pub use jiff::civil::Date as JiffDate; + +#[cfg(feature = "with-jiff")] +pub use jiff::civil::DateTime as JiffDateTime; + +#[cfg(feature = "with-jiff")] +pub use jiff::civil::Time as JiffTime; + +#[cfg(feature = "with-jiff")] +pub use crate::value::{JiffUnixTimestamp, JiffUnixTimestampMillis}; + #[cfg(feature = "with-rust_decimal")] pub use rust_decimal::Decimal; diff --git a/sea-orm-sync/src/executor/query.rs b/sea-orm-sync/src/executor/query.rs index dbd35e83b4..ab8a504ae3 100644 --- a/sea-orm-sync/src/executor/query.rs +++ b/sea-orm-sync/src/executor/query.rs @@ -412,6 +412,57 @@ macro_rules! try_getable_all { }; } +#[cfg(feature = "with-jiff")] +macro_rules! try_getable_jiff { + ($(($type: ty, $wrapper: ty)),+ $(,)?) => { + $( + impl TryGetable for $type { + #[allow(unused_variables)] + fn try_get_by(res: &QueryResult, idx: I) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(_) => Err(type_err(format!( + "{} unsupported by sqlx-mysql", + stringify!($type) + )) + .into()), + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => row + .try_get::, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| { + opt.map(Into::into).ok_or_else(|| err_null_idx_col(idx)) + }), + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => row + .try_get::, _>(idx.as_sqlx_sqlite_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| { + opt.map(Into::into).ok_or_else(|| err_null_idx_col(idx)) + }), + #[cfg(feature = "rusqlite")] + QueryResultRow::Rusqlite(row) => row + .try_get::, _>(idx) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[cfg(feature = "proxy")] + QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } + } + )+ + }; +} + macro_rules! try_getable_unsigned { ( $type: ty ) => { impl TryGetable for $type { @@ -649,6 +700,14 @@ try_getable_all!(time::PrimitiveDateTime); #[cfg(feature = "with-time")] try_getable_all!(time::OffsetDateTime); +#[cfg(feature = "with-jiff")] +try_getable_jiff!( + (jiff::civil::Date, jiff_sqlx::Date), + (jiff::civil::Time, jiff_sqlx::Time), + (jiff::civil::DateTime, jiff_sqlx::DateTime), + (jiff::Timestamp, jiff_sqlx::Timestamp), +); + #[cfg(feature = "with-rust_decimal")] use rust_decimal::Decimal; @@ -1048,6 +1107,59 @@ mod postgres_array { }; } + #[cfg(feature = "with-jiff")] + macro_rules! try_getable_postgres_jiff_array { + ( $type: ty, $wrapper: ty ) => { + #[allow(unused_variables)] + impl TryGetable for Vec<$type> { + fn try_get_by(res: &QueryResult, idx: I) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(_) => Err(type_err(format!( + "{} unsupported by sqlx-mysql", + stringify!($type) + )) + .into()), + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => row + .try_get::>, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| { + opt.map(|vals| vals.into_iter().map(Into::into).collect()) + .ok_or_else(|| err_null_idx_col(idx)) + }), + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(_) => Err(type_err(format!( + "{} unsupported by sqlx-sqlite", + stringify!($type) + )) + .into()), + #[cfg(feature = "rusqlite")] + QueryResultRow::Rusqlite(_) => Err(type_err(format!( + "{} unsupported by rusqlite", + stringify!($type) + )) + .into()), + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[cfg(feature = "proxy")] + #[allow(unused_variables)] + QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } + } + }; + } + try_getable_postgres_array!(bool); try_getable_postgres_array!(i8); try_getable_postgres_array!(i16); @@ -1091,6 +1203,18 @@ mod postgres_array { #[cfg(feature = "with-time")] try_getable_postgres_array!(time::OffsetDateTime); + #[cfg(feature = "with-jiff")] + try_getable_postgres_jiff_array!(jiff::civil::Date, jiff_sqlx::Date); + + #[cfg(feature = "with-jiff")] + try_getable_postgres_jiff_array!(jiff::civil::Time, jiff_sqlx::Time); + + #[cfg(feature = "with-jiff")] + try_getable_postgres_jiff_array!(jiff::civil::DateTime, jiff_sqlx::DateTime); + + #[cfg(feature = "with-jiff")] + try_getable_postgres_jiff_array!(jiff::Timestamp, jiff_sqlx::Timestamp); + #[cfg(feature = "with-rust_decimal")] try_getable_postgres_array!(rust_decimal::Decimal); @@ -1643,6 +1767,18 @@ try_from_u64_err!(time::PrimitiveDateTime); #[cfg(feature = "with-time")] try_from_u64_err!(time::OffsetDateTime); +#[cfg(feature = "with-jiff")] +try_from_u64_err!(jiff::civil::Date); + +#[cfg(feature = "with-jiff")] +try_from_u64_err!(jiff::civil::Time); + +#[cfg(feature = "with-jiff")] +try_from_u64_err!(jiff::civil::DateTime); + +#[cfg(feature = "with-jiff")] +try_from_u64_err!(jiff::Timestamp); + #[cfg(feature = "with-rust_decimal")] try_from_u64_err!(rust_decimal::Decimal); diff --git a/sea-orm-sync/src/query/json.rs b/sea-orm-sync/src/query/json.rs index a9e7b6e503..dea4d3d1da 100644 --- a/sea-orm-sync/src/query/json.rs +++ b/sea-orm-sync/src/query/json.rs @@ -110,6 +110,44 @@ impl FromQueryResult for JsonValue { } }; } + #[cfg(feature = "with-jiff")] + macro_rules! match_postgres_jiff_type { + ( $type: ty, $wrapper: ty ) => { + match col_type.kind() { + #[cfg(feature = "postgres-array")] + sqlx::postgres::PgTypeKind::Array(_) => { + if as Type>::type_info().eq(col_type) { + if let Ok(v) = row + .try_get::>, _>(column.ordinal()) + { + map.insert( + col.to_owned(), + json!(v.map(|vals| { + vals.into_iter() + .map(Into::into) + .collect::>() + })), + ); + continue; + } + } + } + _ => { + if <$wrapper as Type>::type_info().eq(col_type) { + if let Ok(v) = + row.try_get::, _>(column.ordinal()) + { + map.insert( + col.to_owned(), + json!(v.map(|v| <$type>::from(v))), + ); + continue; + } + } + } + } + }; + } match_postgres_type!(bool); match_postgres_type!(i8); @@ -142,6 +180,14 @@ impl FromQueryResult for JsonValue { match_postgres_type!(time::PrimitiveDateTime); #[cfg(feature = "with-time")] match_postgres_type!(time::OffsetDateTime); + #[cfg(feature = "with-jiff")] + match_postgres_jiff_type!(jiff::civil::Date, jiff_sqlx::Date); + #[cfg(feature = "with-jiff")] + match_postgres_jiff_type!(jiff::civil::Time, jiff_sqlx::Time); + #[cfg(feature = "with-jiff")] + match_postgres_jiff_type!(jiff::civil::DateTime, jiff_sqlx::DateTime); + #[cfg(feature = "with-jiff")] + match_postgres_jiff_type!(jiff::Timestamp, jiff_sqlx::Timestamp); #[cfg(feature = "with-rust_decimal")] match_postgres_type!(rust_decimal::Decimal); #[cfg(feature = "with-json")] @@ -183,6 +229,18 @@ impl FromQueryResult for JsonValue { } }; } + #[cfg(feature = "with-jiff")] + macro_rules! match_sqlite_jiff_type { + ( $type: ty, $wrapper: ty ) => { + if <$wrapper as Type>::type_info().eq(col_type) { + if let Ok(v) = row.try_get::, _>(column.ordinal()) + { + map.insert(col.to_owned(), json!(v.map(|v| <$type>::from(v)))); + continue; + } + } + }; + } match_sqlite_type!(bool); match_sqlite_type!(i8); match_sqlite_type!(i16); @@ -208,6 +266,14 @@ impl FromQueryResult for JsonValue { match_sqlite_type!(time::PrimitiveDateTime); #[cfg(feature = "with-time")] match_sqlite_type!(time::OffsetDateTime); + #[cfg(feature = "with-jiff")] + match_sqlite_jiff_type!(jiff::civil::Date, jiff_sqlx::Date); + #[cfg(feature = "with-jiff")] + match_sqlite_jiff_type!(jiff::civil::Time, jiff_sqlx::Time); + #[cfg(feature = "with-jiff")] + match_sqlite_jiff_type!(jiff::civil::DateTime, jiff_sqlx::DateTime); + #[cfg(feature = "with-jiff")] + match_sqlite_jiff_type!(jiff::Timestamp, jiff_sqlx::Timestamp); try_get_type!(String, col); #[cfg(feature = "with-uuid")] try_get_type!(uuid::Uuid, col); diff --git a/sea-orm-sync/src/value.rs b/sea-orm-sync/src/value.rs index 3adb05c990..803c5af1b1 100644 --- a/sea-orm-sync/src/value.rs +++ b/sea-orm-sync/src/value.rs @@ -14,6 +14,11 @@ mod with_time; #[cfg(feature = "with-time")] pub use with_time::*; +#[cfg(feature = "with-jiff")] +mod with_jiff; +#[cfg(feature = "with-jiff")] +pub use with_jiff::*; + #[cfg(feature = "with-uuid")] mod text_uuid; #[cfg(feature = "with-uuid")] diff --git a/sea-orm-sync/src/value/with_jiff.rs b/sea-orm-sync/src/value/with_jiff.rs new file mode 100644 index 0000000000..2e5cb0682a --- /dev/null +++ b/sea-orm-sync/src/value/with_jiff.rs @@ -0,0 +1,46 @@ +use super::impl_timestamp; +use crate as sea_orm; +use crate::{DbErr, TryGetError, prelude::JiffTimestamp}; +use std::ops::{Deref, DerefMut}; + +/// A Jiff timestamp mapped to i64 in database +#[derive(derive_more::Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[debug("{_0:?}")] +pub struct JiffUnixTimestamp(pub JiffTimestamp); + +/// A Jiff timestamp mapped to i64 in database, but in milliseconds +#[derive(derive_more::Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[debug("{_0:?}")] +pub struct JiffUnixTimestampMillis(pub JiffTimestamp); + +impl_timestamp!( + JiffUnixTimestamp, + JiffTimestamp, + from_timestamp, + to_timestamp +); + +impl_timestamp!( + JiffUnixTimestampMillis, + JiffTimestamp, + from_timestamp_millis, + to_timestamp_millis +); + +fn from_timestamp(ts: i64) -> Option { + JiffTimestamp::from_second(ts).ok().map(JiffUnixTimestamp) +} + +fn to_timestamp(ts: JiffUnixTimestamp) -> i64 { + ts.0.as_second() +} + +fn from_timestamp_millis(ts: i64) -> Option { + JiffTimestamp::from_millisecond(ts) + .ok() + .map(JiffUnixTimestampMillis) +} + +fn to_timestamp_millis(ts: JiffUnixTimestampMillis) -> i64 { + ts.0.as_millisecond() +} diff --git a/sea-orm-sync/tests/timestamp_tests.rs b/sea-orm-sync/tests/timestamp_tests.rs index 535dd9068b..fed8601fed 100644 --- a/sea-orm-sync/tests/timestamp_tests.rs +++ b/sea-orm-sync/tests/timestamp_tests.rs @@ -31,6 +31,8 @@ mod access_log { pub ms: ChronoUnixTimestampMillis, pub tts: TimeUnixTimestamp, pub tms: TimeUnixTimestampMillis, + pub jts: JiffUnixTimestamp, + pub jms: JiffUnixTimestampMillis, } impl ActiveModelBehavior for ActiveModel {} @@ -50,6 +52,8 @@ fn entity_timestamp_test() -> Result<(), DbErr> { now.timestamp_nanos_opt().unwrap() as i128, ) .unwrap(); + let jiff_seconds = JiffTimestamp::from_second(now.timestamp()).unwrap(); + let jiff_millis = JiffTimestamp::from_millisecond(now.timestamp_millis()).unwrap(); let log = access_log::ActiveModel { id: NotSet, @@ -57,6 +61,8 @@ fn entity_timestamp_test() -> Result<(), DbErr> { ms: Set(ChronoUnixTimestampMillis(now)), tts: Set(TimeUnixTimestamp(time_now)), tms: Set(time_now.into()), + jts: Set(jiff_seconds.into()), + jms: Set(JiffUnixTimestampMillis(jiff_millis)), } .insert(db)?; @@ -68,6 +74,8 @@ fn entity_timestamp_test() -> Result<(), DbErr> { log.tms.unix_timestamp_nanos() / 1_000_000, now.timestamp_millis() as i128 ); + assert_eq!(log.jts.as_second(), jiff_seconds.as_second()); + assert_eq!(log.jms.as_millisecond(), jiff_millis.as_millisecond()); #[derive(DerivePartialModel)] #[sea_orm(entity = "access_log::Entity")] @@ -76,6 +84,8 @@ fn entity_timestamp_test() -> Result<(), DbErr> { ms: i64, tts: i64, tms: i64, + jts: i64, + jms: i64, } let log: AccessLog = access_log::Entity::find() @@ -87,6 +97,8 @@ fn entity_timestamp_test() -> Result<(), DbErr> { assert_eq!(log.ms, now.timestamp_millis()); assert_eq!(log.tts, now.timestamp()); assert_eq!(log.tms, now.timestamp_millis()); + assert_eq!(log.jts, jiff_seconds.as_second()); + assert_eq!(log.jms, jiff_millis.as_millisecond()); Ok(()) } diff --git a/sea-orm-sync/tests/type_tests.rs b/sea-orm-sync/tests/type_tests.rs index c2bda9f693..b21197f8b3 100644 --- a/sea-orm-sync/tests/type_tests.rs +++ b/sea-orm-sync/tests/type_tests.rs @@ -63,6 +63,10 @@ fn main() { it_impl_traits!(time::Time); it_impl_traits!(time::PrimitiveDateTime); it_impl_traits!(time::OffsetDateTime); + it_impl_traits!(jiff::civil::Date); + it_impl_traits!(jiff::civil::Time); + it_impl_traits!(jiff::civil::DateTime); + it_impl_traits!(jiff::Timestamp); it_impl_traits!(rust_decimal::Decimal); it_impl_traits!(uuid::Uuid); #[cfg(feature = "with-ipnetwork")] diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 036fa306b8..27e5ff7b71 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -687,216 +687,411 @@ pub(crate) fn from_sqlx_postgres_row_to_proxy_row(row: &sqlx::postgres::PgRow) - }), ), - #[cfg(feature = "with-chrono")] - "TIMESTAMP" => Value::ChronoDateTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timestamp"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "TIMESTAMP" => Value::TimeDateTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timestamp"), - ), + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] + "TIMESTAMP" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoDateTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeDateTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp"), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::JiffDateTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp") + .map(Into::into) + .map(Box::new), + ); + }; + value + } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "TIMESTAMP[]" => Value::Array( - sea_query::ArrayType::ChronoDateTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamp array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoDateTime(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( - feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "postgres-array", + any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ) ))] - "TIMESTAMP[]" => Value::Array( - sea_query::ArrayType::TimeDateTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamp array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeDateTime(Some(val))) - .collect(), + "TIMESTAMP[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoDateTime, + row.try_get::>, _>( + c.ordinal(), ) - }), - ), + .expect("Failed to get timestamp array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoDateTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeDateTime, + row.try_get::>, _>( + c.ordinal(), + ) + .expect("Failed to get timestamp array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::TimeDateTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::Array( + sea_query::ArrayType::JiffDateTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timestamp array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(Into::::into) + .map(|val| { + Value::JiffDateTime(Some(Box::new(val))) + }) + .collect(), + ) + }), + ); + }; + value + } - #[cfg(feature = "with-chrono")] - "DATE" => Value::ChronoDate( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get date"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "DATE" => Value::TimeDate( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get date"), - ), + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] + "DATE" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date"), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::JiffDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date") + .map(Into::into), + ); + }; + value + } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "DATE[]" => Value::Array( - sea_query::ArrayType::ChronoDate, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get date array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoDate(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( - feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "postgres-array", + any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ) ))] - "DATE[]" => Value::Array( - sea_query::ArrayType::TimeDate, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get date array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeDate(Some(val))) - .collect(), - ) - }), - ), + "DATE[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoDate, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get date array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoDate(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeDate, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get date array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::TimeDate(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::Array( + sea_query::ArrayType::JiffDate, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get date array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(Into::::into) + .map(|val| Value::JiffDate(Some(val))) + .collect(), + ) + }), + ); + }; + value + } - #[cfg(feature = "with-chrono")] - "TIME" => Value::ChronoTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get time"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "TIME" => Value::TimeTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get time"), - ), + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] + "TIME" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time"), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::JiffTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time") + .map(Into::into), + ); + }; + value + } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "TIME[]" => Value::Array( - sea_query::ArrayType::ChronoTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get time array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoTime(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( + feature = "postgres-array", + any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ) + ))] + "TIME[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get time array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get time array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::TimeTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::Array( + sea_query::ArrayType::JiffTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get time array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(Into::::into) + .map(|val| Value::JiffTime(Some(val))) + .collect(), + ) + }), + ); + }; + value + } + + #[cfg(any( + feature = "with-chrono", feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "with-jiff" ))] - "TIME[]" => Value::Array( - sea_query::ArrayType::TimeTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get time array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeTime(Some(val))) - .collect(), + "TIMESTAMPTZ" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoDateTimeUtc( + row.try_get::>, _>( + c.ordinal(), ) - }), - ), - - #[cfg(feature = "with-chrono")] - "TIMESTAMPTZ" => Value::ChronoDateTimeUtc( - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamptz"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "TIMESTAMPTZ" => Value::TimeDateTimeWithTimeZone( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timestamptz"), - ), + .expect("Failed to get timestamptz"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeDateTimeWithTimeZone( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamptz"), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::JiffTimestamp( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamptz") + .map(Into::into) + .map(Box::new), + ); + }; + value + } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "TIMESTAMPTZ[]" => Value::Array( - sea_query::ArrayType::ChronoDateTimeUtc, - row.try_get::>>, _>( - c.ordinal(), - ) - .expect("Failed to get timestamptz array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoDateTimeUtc(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( - feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "postgres-array", + any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ) ))] - "TIMESTAMPTZ[]" => Value::Array( - sea_query::ArrayType::TimeDateTimeWithTimeZone, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamptz array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeDateTimeWithTimeZone(Some(val))) - .collect(), + "TIMESTAMPTZ[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoDateTimeUtc, + row.try_get::>>, _>( + c.ordinal(), ) - }), - ), + .expect("Failed to get timestamptz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoDateTimeUtc(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeDateTimeWithTimeZone, + row.try_get::>, _>( + c.ordinal(), + ) + .expect("Failed to get timestamptz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| { + Value::TimeDateTimeWithTimeZone(Some(val)) + }) + .collect(), + ) + }), + ); + #[cfg(feature = "with-jiff")] + break 'value Value::Array( + sea_query::ArrayType::JiffTimestamp, + row.try_get::>, _>( + c.ordinal(), + ) + .expect("Failed to get timestamptz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(Into::::into) + .map(|val| { + Value::JiffTimestamp(Some(Box::new(val))) + }) + .collect(), + ) + }), + ); + }; + value + } - #[cfg(feature = "with-chrono")] - "TIMETZ" => Value::ChronoTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timetz"), - ), - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + #[cfg(any(feature = "with-chrono", feature = "with-time"))] "TIMETZ" => { - Value::TimeTime(row.try_get(c.ordinal()).expect("Failed to get timetz")) + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::ChronoTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timetz"), + ); + #[cfg(feature = "with-time")] + break 'value Value::TimeTime( + row.try_get(c.ordinal()).expect("Failed to get timetz"), + ); + }; + value } - #[cfg(all(feature = "with-chrono", feature = "postgres-array"))] - "TIMETZ[]" => Value::Array( - sea_query::ArrayType::ChronoTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timetz array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::ChronoTime(Some(val))) - .collect(), - ) - }), - ), #[cfg(all( - feature = "with-time", - not(feature = "with-chrono"), - feature = "postgres-array" + feature = "postgres-array", + any(feature = "with-chrono", feature = "with-time") ))] - "TIMETZ[]" => Value::Array( - sea_query::ArrayType::TimeTime, - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timetz array") - .map(|vals| { - Box::new( - vals.into_iter() - .map(|val| Value::TimeTime(Some(val))) - .collect(), - ) - }), - ), + "TIMETZ[]" => { + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + break 'value Value::Array( + sea_query::ArrayType::ChronoTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timetz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::ChronoTime(Some(val))) + .collect(), + ) + }), + ); + #[cfg(feature = "with-time")] + break 'value Value::Array( + sea_query::ArrayType::TimeTime, + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timetz array") + .map(|vals| { + Box::new( + vals.into_iter() + .map(|val| Value::TimeTime(Some(val))) + .collect(), + ) + }), + ); + }; + value + } #[cfg(feature = "with-uuid")] "UUID" => Value::Uuid( diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index e299743e6f..24366407ed 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -421,61 +421,112 @@ pub(crate) fn from_sqlx_sqlite_row_to_proxy_row(row: &sqlx::sqlite::SqliteRow) - .map(Box::new), ), - #[cfg(feature = "with-chrono")] + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] "DATETIME" => { - use chrono::{DateTime, Utc}; - - Value::ChronoDateTimeUtc( - row.try_get::>, _>(c.ordinal()) - .expect("Failed to get timestamp") - .map(Box::new), - ) - } - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "DATETIME" => { - use time::OffsetDateTime; - Value::TimeDateTimeWithTimeZone( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get timestamp") - .map(Box::new), - ) + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + { + use chrono::{DateTime, Utc}; + + break 'value Value::ChronoDateTimeUtc( + row.try_get::>, _>(c.ordinal()) + .expect("Failed to get timestamp"), + ); + } + #[cfg(feature = "with-time")] + { + use time::OffsetDateTime; + + break 'value Value::TimeDateTimeWithTimeZone( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp"), + ); + } + #[cfg(feature = "with-jiff")] + break 'value Value::JiffDateTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get timestamp") + .map(Into::into) + .map(Box::new), + ); + }; + value } - #[cfg(feature = "with-chrono")] + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] "DATE" => { - use chrono::NaiveDate; - Value::ChronoDate( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get date") - .map(Box::new), - ) - } - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] - "DATE" => { - use time::Date; - Value::TimeDate( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get date") - .map(Box::new), - ) + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + { + use chrono::NaiveDate; + + break 'value Value::ChronoDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date"), + ); + } + #[cfg(feature = "with-time")] + { + use time::Date; + + break 'value Value::TimeDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date"), + ); + } + #[cfg(feature = "with-jiff")] + break 'value Value::JiffDate( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get date") + .map(Into::into), + ); + }; + value } - #[cfg(feature = "with-chrono")] - "TIME" => { - use chrono::NaiveTime; - Value::ChronoTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get time") - .map(Box::new), - ) - } - #[cfg(all(feature = "with-time", not(feature = "with-chrono")))] + #[cfg(any( + feature = "with-chrono", + feature = "with-time", + feature = "with-jiff" + ))] "TIME" => { - use time::Time; - Value::TimeTime( - row.try_get::, _>(c.ordinal()) - .expect("Failed to get time") - .map(Box::new), - ) + #[allow(unreachable_code)] + let value = 'value: { + #[cfg(feature = "with-chrono")] + { + use chrono::NaiveTime; + + break 'value Value::ChronoTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time"), + ); + } + #[cfg(feature = "with-time")] + { + use time::Time; + + break 'value Value::TimeTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time"), + ); + } + #[cfg(feature = "with-jiff")] + break 'value Value::JiffTime( + row.try_get::, _>(c.ordinal()) + .expect("Failed to get time") + .map(Into::into), + ); + }; + value } _ => unreachable!("Unknown column type: {}", c.type_info().name()), diff --git a/src/dynamic/model.rs b/src/dynamic/model.rs index fe3fe30518..406eafd7d7 100644 --- a/src/dynamic/model.rs +++ b/src/dynamic/model.rs @@ -84,6 +84,7 @@ fn try_get(res: &QueryResult, pre: &str, col: &str, ty: &ArrayType) -> Result Value::String(res.try_get(pre, col)?), ArrayType::Char => return Err(DbErr::Type("Unsupported type: char".into())), ArrayType::Bytes => Value::Bytes(res.try_get(pre, col)?), + ArrayType::Enum(_) => Value::String(res.try_get(pre, col)?), #[cfg(feature = "with-json")] ArrayType::Json => Value::Json(res.try_get::>(pre, col)?.map(Box::new)), @@ -122,6 +123,24 @@ fn try_get(res: &QueryResult, pre: &str, col: &str, ty: &ArrayType) -> Result Value::JiffDate(res.try_get(pre, col)?), + + #[cfg(feature = "with-jiff")] + ArrayType::JiffTime => Value::JiffTime(res.try_get(pre, col)?), + + #[cfg(feature = "with-jiff")] + ArrayType::JiffDateTime => Value::JiffDateTime( + res.try_get::>(pre, col)? + .map(Box::new), + ), + + #[cfg(feature = "with-jiff")] + ArrayType::JiffTimestamp => Value::JiffTimestamp( + res.try_get::>(pre, col)? + .map(Box::new), + ), + #[cfg(feature = "with-uuid")] ArrayType::Uuid => Value::Uuid(res.try_get(pre, col)?), diff --git a/src/entity/active_value.rs b/src/entity/active_value.rs index 36f4186a0a..f5920756c4 100644 --- a/src/entity/active_value.rs +++ b/src/entity/active_value.rs @@ -132,29 +132,33 @@ where } macro_rules! impl_into_active_value { - ($ty: ty) => { - impl IntoActiveValue<$ty> for $ty { - fn into_active_value(self) -> ActiveValue<$ty> { - Set(self) + ($( $ty: ty ),+ $(,)?) => { + $( + impl IntoActiveValue<$ty> for $ty { + fn into_active_value(self) -> ActiveValue<$ty> { + Set(self) + } } - } + )+ }; } -impl_into_active_value!(bool); -impl_into_active_value!(i8); -impl_into_active_value!(i16); -impl_into_active_value!(i32); -impl_into_active_value!(i64); -impl_into_active_value!(u8); -impl_into_active_value!(u16); -impl_into_active_value!(u32); -impl_into_active_value!(u64); -impl_into_active_value!(f32); -impl_into_active_value!(f64); -impl_into_active_value!(&'static str); -impl_into_active_value!(String); -impl_into_active_value!(Vec); +impl_into_active_value!( + bool, + i8, + i16, + i32, + i64, + u8, + u16, + u32, + u64, + f32, + f64, + &'static str, + String, + Vec, +); #[cfg(feature = "with-json")] #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] @@ -162,27 +166,14 @@ impl_into_active_value!(crate::prelude::Json); #[cfg(feature = "with-chrono")] #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::Date); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::Time); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::DateTime); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::DateTimeWithTimeZone); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::DateTimeUtc); - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -impl_into_active_value!(crate::prelude::DateTimeLocal); +impl_into_active_value!( + crate::prelude::Date, + crate::prelude::Time, + crate::prelude::DateTime, + crate::prelude::DateTimeWithTimeZone, + crate::prelude::DateTimeUtc, + crate::prelude::DateTimeLocal, +); #[cfg(feature = "with-rust_decimal")] #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] @@ -198,19 +189,21 @@ impl_into_active_value!(crate::prelude::Uuid); #[cfg(feature = "with-time")] #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -impl_into_active_value!(crate::prelude::TimeDate); - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -impl_into_active_value!(crate::prelude::TimeTime); - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -impl_into_active_value!(crate::prelude::TimeDateTime); - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -impl_into_active_value!(crate::prelude::TimeDateTimeWithTimeZone); +impl_into_active_value!( + crate::prelude::TimeDate, + crate::prelude::TimeTime, + crate::prelude::TimeDateTime, + crate::prelude::TimeDateTimeWithTimeZone, +); + +#[cfg(feature = "with-jiff")] +#[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] +impl_into_active_value!( + crate::prelude::JiffDate, + crate::prelude::JiffTime, + crate::prelude::JiffDateTime, + crate::prelude::JiffTimestamp, +); #[cfg(feature = "with-ipnetwork")] #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] diff --git a/src/entity/column/types.rs b/src/entity/column/types.rs index d0c90e2e77..6e9661ab1f 100644 --- a/src/entity/column/types.rs +++ b/src/entity/column/types.rs @@ -76,7 +76,7 @@ impl_expr_traits!(GenericArrayColumn); #[cfg(feature = "with-json")] mod with_json; -#[cfg(any(feature = "with-chrono", feature = "with-time"))] +#[cfg(any(feature = "with-chrono", feature = "with-time", feature = "with-jiff"))] mod with_datetime; #[cfg(feature = "with-uuid")] diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 6e3e2cbc13..9ab4eef5e1 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -89,6 +89,21 @@ pub use time::OffsetDateTime as TimeDateTimeWithTimeZone; #[cfg(feature = "with-time")] pub use crate::value::{TimeUnixTimestamp, TimeUnixTimestampMillis}; +#[cfg(feature = "with-jiff")] +pub use jiff::Timestamp as JiffTimestamp; + +#[cfg(feature = "with-jiff")] +pub use jiff::civil::Date as JiffDate; + +#[cfg(feature = "with-jiff")] +pub use jiff::civil::DateTime as JiffDateTime; + +#[cfg(feature = "with-jiff")] +pub use jiff::civil::Time as JiffTime; + +#[cfg(feature = "with-jiff")] +pub use crate::value::{JiffUnixTimestamp, JiffUnixTimestampMillis}; + #[cfg(feature = "with-rust_decimal")] pub use rust_decimal::Decimal; diff --git a/src/executor/query.rs b/src/executor/query.rs index 16667d9ab6..931ba25fc1 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -412,6 +412,57 @@ macro_rules! try_getable_all { }; } +#[cfg(feature = "with-jiff")] +macro_rules! try_getable_jiff { + ($(($type: ty, $wrapper: ty)),+ $(,)?) => { + $( + impl TryGetable for $type { + #[allow(unused_variables)] + fn try_get_by(res: &QueryResult, idx: I) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(_) => Err(type_err(format!( + "{} unsupported by sqlx-mysql", + stringify!($type) + )) + .into()), + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => row + .try_get::, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| { + opt.map(Into::into).ok_or_else(|| err_null_idx_col(idx)) + }), + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(row) => row + .try_get::, _>(idx.as_sqlx_sqlite_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| { + opt.map(Into::into).ok_or_else(|| err_null_idx_col(idx)) + }), + #[cfg(feature = "rusqlite")] + QueryResultRow::Rusqlite(row) => row + .try_get::, _>(idx) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[cfg(feature = "proxy")] + QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } + } + )+ + }; +} + macro_rules! try_getable_unsigned { ( $type: ty ) => { impl TryGetable for $type { @@ -649,6 +700,14 @@ try_getable_all!(time::PrimitiveDateTime); #[cfg(feature = "with-time")] try_getable_all!(time::OffsetDateTime); +#[cfg(feature = "with-jiff")] +try_getable_jiff!( + (jiff::civil::Date, jiff_sqlx::Date), + (jiff::civil::Time, jiff_sqlx::Time), + (jiff::civil::DateTime, jiff_sqlx::DateTime), + (jiff::Timestamp, jiff_sqlx::Timestamp), +); + #[cfg(feature = "with-rust_decimal")] use rust_decimal::Decimal; @@ -1048,6 +1107,59 @@ mod postgres_array { }; } + #[cfg(feature = "with-jiff")] + macro_rules! try_getable_postgres_jiff_array { + ( $type: ty, $wrapper: ty ) => { + #[allow(unused_variables)] + impl TryGetable for Vec<$type> { + fn try_get_by(res: &QueryResult, idx: I) -> Result { + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(_) => Err(type_err(format!( + "{} unsupported by sqlx-mysql", + stringify!($type) + )) + .into()), + #[cfg(feature = "sqlx-postgres")] + QueryResultRow::SqlxPostgres(row) => row + .try_get::>, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| { + opt.map(|vals| vals.into_iter().map(Into::into).collect()) + .ok_or_else(|| err_null_idx_col(idx)) + }), + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(_) => Err(type_err(format!( + "{} unsupported by sqlx-sqlite", + stringify!($type) + )) + .into()), + #[cfg(feature = "rusqlite")] + QueryResultRow::Rusqlite(_) => Err(type_err(format!( + "{} unsupported by rusqlite", + stringify!($type) + )) + .into()), + #[cfg(feature = "mock")] + #[allow(unused_variables)] + QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[cfg(feature = "proxy")] + #[allow(unused_variables)] + QueryResultRow::Proxy(row) => row.try_get(idx).map_err(|e| { + debug_print!("{:#?}", e.to_string()); + err_null_idx_col(idx) + }), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } + } + }; + } + try_getable_postgres_array!(bool); try_getable_postgres_array!(i8); try_getable_postgres_array!(i16); @@ -1091,6 +1203,18 @@ mod postgres_array { #[cfg(feature = "with-time")] try_getable_postgres_array!(time::OffsetDateTime); + #[cfg(feature = "with-jiff")] + try_getable_postgres_jiff_array!(jiff::civil::Date, jiff_sqlx::Date); + + #[cfg(feature = "with-jiff")] + try_getable_postgres_jiff_array!(jiff::civil::Time, jiff_sqlx::Time); + + #[cfg(feature = "with-jiff")] + try_getable_postgres_jiff_array!(jiff::civil::DateTime, jiff_sqlx::DateTime); + + #[cfg(feature = "with-jiff")] + try_getable_postgres_jiff_array!(jiff::Timestamp, jiff_sqlx::Timestamp); + #[cfg(feature = "with-rust_decimal")] try_getable_postgres_array!(rust_decimal::Decimal); @@ -1645,6 +1769,18 @@ try_from_u64_err!(time::PrimitiveDateTime); #[cfg(feature = "with-time")] try_from_u64_err!(time::OffsetDateTime); +#[cfg(feature = "with-jiff")] +try_from_u64_err!(jiff::civil::Date); + +#[cfg(feature = "with-jiff")] +try_from_u64_err!(jiff::civil::Time); + +#[cfg(feature = "with-jiff")] +try_from_u64_err!(jiff::civil::DateTime); + +#[cfg(feature = "with-jiff")] +try_from_u64_err!(jiff::Timestamp); + #[cfg(feature = "with-rust_decimal")] try_from_u64_err!(rust_decimal::Decimal); diff --git a/src/query/json.rs b/src/query/json.rs index c71548971d..a244e225fa 100644 --- a/src/query/json.rs +++ b/src/query/json.rs @@ -110,6 +110,44 @@ impl FromQueryResult for JsonValue { } }; } + #[cfg(feature = "with-jiff")] + macro_rules! match_postgres_jiff_type { + ( $type: ty, $wrapper: ty ) => { + match col_type.kind() { + #[cfg(feature = "postgres-array")] + sqlx::postgres::PgTypeKind::Array(_) => { + if as Type>::type_info().eq(col_type) { + if let Ok(v) = row + .try_get::>, _>(column.ordinal()) + { + map.insert( + col.to_owned(), + json!(v.map(|vals| { + vals.into_iter() + .map(Into::into) + .collect::>() + })), + ); + continue; + } + } + } + _ => { + if <$wrapper as Type>::type_info().eq(col_type) { + if let Ok(v) = + row.try_get::, _>(column.ordinal()) + { + map.insert( + col.to_owned(), + json!(v.map(|v| <$type>::from(v))), + ); + continue; + } + } + } + } + }; + } match_postgres_type!(bool); match_postgres_type!(i8); @@ -142,6 +180,14 @@ impl FromQueryResult for JsonValue { match_postgres_type!(time::PrimitiveDateTime); #[cfg(feature = "with-time")] match_postgres_type!(time::OffsetDateTime); + #[cfg(feature = "with-jiff")] + match_postgres_jiff_type!(jiff::civil::Date, jiff_sqlx::Date); + #[cfg(feature = "with-jiff")] + match_postgres_jiff_type!(jiff::civil::Time, jiff_sqlx::Time); + #[cfg(feature = "with-jiff")] + match_postgres_jiff_type!(jiff::civil::DateTime, jiff_sqlx::DateTime); + #[cfg(feature = "with-jiff")] + match_postgres_jiff_type!(jiff::Timestamp, jiff_sqlx::Timestamp); #[cfg(feature = "with-rust_decimal")] match_postgres_type!(rust_decimal::Decimal); #[cfg(feature = "with-json")] @@ -183,6 +229,18 @@ impl FromQueryResult for JsonValue { } }; } + #[cfg(feature = "with-jiff")] + macro_rules! match_sqlite_jiff_type { + ( $type: ty, $wrapper: ty ) => { + if <$wrapper as Type>::type_info().eq(col_type) { + if let Ok(v) = row.try_get::, _>(column.ordinal()) + { + map.insert(col.to_owned(), json!(v.map(|v| <$type>::from(v)))); + continue; + } + } + }; + } match_sqlite_type!(bool); match_sqlite_type!(i8); match_sqlite_type!(i16); @@ -208,6 +266,14 @@ impl FromQueryResult for JsonValue { match_sqlite_type!(time::PrimitiveDateTime); #[cfg(feature = "with-time")] match_sqlite_type!(time::OffsetDateTime); + #[cfg(feature = "with-jiff")] + match_sqlite_jiff_type!(jiff::civil::Date, jiff_sqlx::Date); + #[cfg(feature = "with-jiff")] + match_sqlite_jiff_type!(jiff::civil::Time, jiff_sqlx::Time); + #[cfg(feature = "with-jiff")] + match_sqlite_jiff_type!(jiff::civil::DateTime, jiff_sqlx::DateTime); + #[cfg(feature = "with-jiff")] + match_sqlite_jiff_type!(jiff::Timestamp, jiff_sqlx::Timestamp); try_get_type!(String, col); #[cfg(feature = "with-uuid")] try_get_type!(uuid::Uuid, col); diff --git a/src/value.rs b/src/value.rs index 3adb05c990..803c5af1b1 100644 --- a/src/value.rs +++ b/src/value.rs @@ -14,6 +14,11 @@ mod with_time; #[cfg(feature = "with-time")] pub use with_time::*; +#[cfg(feature = "with-jiff")] +mod with_jiff; +#[cfg(feature = "with-jiff")] +pub use with_jiff::*; + #[cfg(feature = "with-uuid")] mod text_uuid; #[cfg(feature = "with-uuid")] diff --git a/src/value/with_jiff.rs b/src/value/with_jiff.rs new file mode 100644 index 0000000000..2e5cb0682a --- /dev/null +++ b/src/value/with_jiff.rs @@ -0,0 +1,46 @@ +use super::impl_timestamp; +use crate as sea_orm; +use crate::{DbErr, TryGetError, prelude::JiffTimestamp}; +use std::ops::{Deref, DerefMut}; + +/// A Jiff timestamp mapped to i64 in database +#[derive(derive_more::Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[debug("{_0:?}")] +pub struct JiffUnixTimestamp(pub JiffTimestamp); + +/// A Jiff timestamp mapped to i64 in database, but in milliseconds +#[derive(derive_more::Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[debug("{_0:?}")] +pub struct JiffUnixTimestampMillis(pub JiffTimestamp); + +impl_timestamp!( + JiffUnixTimestamp, + JiffTimestamp, + from_timestamp, + to_timestamp +); + +impl_timestamp!( + JiffUnixTimestampMillis, + JiffTimestamp, + from_timestamp_millis, + to_timestamp_millis +); + +fn from_timestamp(ts: i64) -> Option { + JiffTimestamp::from_second(ts).ok().map(JiffUnixTimestamp) +} + +fn to_timestamp(ts: JiffUnixTimestamp) -> i64 { + ts.0.as_second() +} + +fn from_timestamp_millis(ts: i64) -> Option { + JiffTimestamp::from_millisecond(ts) + .ok() + .map(JiffUnixTimestampMillis) +} + +fn to_timestamp_millis(ts: JiffUnixTimestampMillis) -> i64 { + ts.0.as_millisecond() +} diff --git a/tests/timestamp_tests.rs b/tests/timestamp_tests.rs index ab9d16a2e0..dfa7d1b234 100644 --- a/tests/timestamp_tests.rs +++ b/tests/timestamp_tests.rs @@ -31,6 +31,8 @@ mod access_log { pub ms: ChronoUnixTimestampMillis, pub tts: TimeUnixTimestamp, pub tms: TimeUnixTimestampMillis, + pub jts: JiffUnixTimestamp, + pub jms: JiffUnixTimestampMillis, } impl ActiveModelBehavior for ActiveModel {} @@ -51,6 +53,8 @@ async fn entity_timestamp_test() -> Result<(), DbErr> { now.timestamp_nanos_opt().unwrap() as i128, ) .unwrap(); + let jiff_seconds = JiffTimestamp::from_second(now.timestamp()).unwrap(); + let jiff_millis = JiffTimestamp::from_millisecond(now.timestamp_millis()).unwrap(); let log = access_log::ActiveModel { id: NotSet, @@ -58,6 +62,8 @@ async fn entity_timestamp_test() -> Result<(), DbErr> { ms: Set(ChronoUnixTimestampMillis(now)), tts: Set(TimeUnixTimestamp(time_now)), tms: Set(time_now.into()), + jts: Set(jiff_seconds.into()), + jms: Set(JiffUnixTimestampMillis(jiff_millis)), } .insert(db) .await?; @@ -70,6 +76,8 @@ async fn entity_timestamp_test() -> Result<(), DbErr> { log.tms.unix_timestamp_nanos() / 1_000_000, now.timestamp_millis() as i128 ); + assert_eq!(log.jts.as_second(), jiff_seconds.as_second()); + assert_eq!(log.jms.as_millisecond(), jiff_millis.as_millisecond()); #[derive(DerivePartialModel)] #[sea_orm(entity = "access_log::Entity")] @@ -78,6 +86,8 @@ async fn entity_timestamp_test() -> Result<(), DbErr> { ms: i64, tts: i64, tms: i64, + jts: i64, + jms: i64, } let log: AccessLog = access_log::Entity::find() @@ -90,6 +100,8 @@ async fn entity_timestamp_test() -> Result<(), DbErr> { assert_eq!(log.ms, now.timestamp_millis()); assert_eq!(log.tts, now.timestamp()); assert_eq!(log.tms, now.timestamp_millis()); + assert_eq!(log.jts, jiff_seconds.as_second()); + assert_eq!(log.jms, jiff_millis.as_millisecond()); Ok(()) } diff --git a/tests/type_tests.rs b/tests/type_tests.rs index c2bda9f693..b21197f8b3 100644 --- a/tests/type_tests.rs +++ b/tests/type_tests.rs @@ -63,6 +63,10 @@ fn main() { it_impl_traits!(time::Time); it_impl_traits!(time::PrimitiveDateTime); it_impl_traits!(time::OffsetDateTime); + it_impl_traits!(jiff::civil::Date); + it_impl_traits!(jiff::civil::Time); + it_impl_traits!(jiff::civil::DateTime); + it_impl_traits!(jiff::Timestamp); it_impl_traits!(rust_decimal::Decimal); it_impl_traits!(uuid::Uuid); #[cfg(feature = "with-ipnetwork")]